libjxl

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

encode.cc (45649B)


      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/encode.h"
      7 
      8 #include <jxl/types.h>
      9 
     10 #include <cmath>
     11 #include <initializer_list>
     12 #include <vector>
     13 
     14 #include "lib/jpegli/adaptive_quantization.h"
     15 #include "lib/jpegli/bit_writer.h"
     16 #include "lib/jpegli/bitstream.h"
     17 #include "lib/jpegli/color_transform.h"
     18 #include "lib/jpegli/downsample.h"
     19 #include "lib/jpegli/encode_finish.h"
     20 #include "lib/jpegli/encode_internal.h"
     21 #include "lib/jpegli/encode_streaming.h"
     22 #include "lib/jpegli/entropy_coding.h"
     23 #include "lib/jpegli/error.h"
     24 #include "lib/jpegli/huffman.h"
     25 #include "lib/jpegli/input.h"
     26 #include "lib/jpegli/memory_manager.h"
     27 #include "lib/jpegli/quant.h"
     28 
     29 namespace jpegli {
     30 
     31 constexpr size_t kMaxBytesInMarker = 65533;
     32 
     33 void CheckState(j_compress_ptr cinfo, int state) {
     34   if (cinfo->global_state != state) {
     35     JPEGLI_ERROR("Unexpected global state %d [expected %d]",
     36                  cinfo->global_state, state);
     37   }
     38 }
     39 
     40 void CheckState(j_compress_ptr cinfo, int state1, int state2) {
     41   if (cinfo->global_state != state1 && cinfo->global_state != state2) {
     42     JPEGLI_ERROR("Unexpected global state %d [expected %d or %d]",
     43                  cinfo->global_state, state1, state2);
     44   }
     45 }
     46 
     47 //
     48 // Parameter setup
     49 //
     50 
     51 // Initialize cinfo fields that are not dependent on input image. This is shared
     52 // between jpegli_CreateCompress() and jpegli_set_defaults()
     53 void InitializeCompressParams(j_compress_ptr cinfo) {
     54   cinfo->data_precision = 8;
     55   cinfo->num_scans = 0;
     56   cinfo->scan_info = nullptr;
     57   cinfo->raw_data_in = FALSE;
     58   cinfo->arith_code = FALSE;
     59   cinfo->optimize_coding = FALSE;
     60   cinfo->CCIR601_sampling = FALSE;
     61   cinfo->smoothing_factor = 0;
     62   cinfo->dct_method = JDCT_FLOAT;
     63   cinfo->restart_interval = 0;
     64   cinfo->restart_in_rows = 0;
     65   cinfo->write_JFIF_header = FALSE;
     66   cinfo->JFIF_major_version = 1;
     67   cinfo->JFIF_minor_version = 1;
     68   cinfo->density_unit = 0;
     69   cinfo->X_density = 1;
     70   cinfo->Y_density = 1;
     71 #if JPEG_LIB_VERSION >= 70
     72   cinfo->scale_num = 1;
     73   cinfo->scale_denom = 1;
     74   cinfo->do_fancy_downsampling = FALSE;
     75   cinfo->min_DCT_h_scaled_size = DCTSIZE;
     76   cinfo->min_DCT_v_scaled_size = DCTSIZE;
     77 #endif
     78   cinfo->master->psnr_target = 0.0f;
     79   cinfo->master->psnr_tolerance = 0.01f;
     80   cinfo->master->min_distance = 0.1f;
     81   cinfo->master->max_distance = 25.0f;
     82 }
     83 
     84 float LinearQualityToDistance(int scale_factor) {
     85   scale_factor = std::min(5000, std::max(0, scale_factor));
     86   int quality =
     87       scale_factor < 100 ? 100 - scale_factor / 2 : 5000 / scale_factor;
     88   return jpegli_quality_to_distance(quality);
     89 }
     90 
     91 template <typename T>
     92 void SetSentTableFlag(T** table_ptrs, size_t num, boolean val) {
     93   for (size_t i = 0; i < num; ++i) {
     94     if (table_ptrs[i]) table_ptrs[i]->sent_table = val;
     95   }
     96 }
     97 
     98 //
     99 // Compressor initialization
    100 //
    101 
    102 struct ProgressiveScan {
    103   int Ss, Se, Ah, Al;
    104   bool interleaved;
    105 };
    106 
    107 void SetDefaultScanScript(j_compress_ptr cinfo) {
    108   int level = cinfo->master->progressive_level;
    109   std::vector<ProgressiveScan> progressive_mode;
    110   bool interleave_dc =
    111       (cinfo->max_h_samp_factor == 1 && cinfo->max_v_samp_factor == 1);
    112   if (level == 0) {
    113     progressive_mode.push_back({0, 63, 0, 0, true});
    114   } else if (level == 1) {
    115     progressive_mode.push_back({0, 0, 0, 0, interleave_dc});
    116     progressive_mode.push_back({1, 63, 0, 1, false});
    117     progressive_mode.push_back({1, 63, 1, 0, false});
    118   } else {
    119     progressive_mode.push_back({0, 0, 0, 0, interleave_dc});
    120     progressive_mode.push_back({1, 2, 0, 0, false});
    121     progressive_mode.push_back({3, 63, 0, 2, false});
    122     progressive_mode.push_back({3, 63, 2, 1, false});
    123     progressive_mode.push_back({3, 63, 1, 0, false});
    124   }
    125 
    126   cinfo->script_space_size = 0;
    127   for (const auto& scan : progressive_mode) {
    128     int comps = scan.interleaved ? MAX_COMPS_IN_SCAN : 1;
    129     cinfo->script_space_size += DivCeil(cinfo->num_components, comps);
    130   }
    131   cinfo->script_space =
    132       Allocate<jpeg_scan_info>(cinfo, cinfo->script_space_size);
    133 
    134   jpeg_scan_info* next_scan = cinfo->script_space;
    135   for (const auto& scan : progressive_mode) {
    136     int comps = scan.interleaved ? MAX_COMPS_IN_SCAN : 1;
    137     for (int c = 0; c < cinfo->num_components; c += comps) {
    138       next_scan->Ss = scan.Ss;
    139       next_scan->Se = scan.Se;
    140       next_scan->Ah = scan.Ah;
    141       next_scan->Al = scan.Al;
    142       next_scan->comps_in_scan = std::min(comps, cinfo->num_components - c);
    143       for (int j = 0; j < next_scan->comps_in_scan; ++j) {
    144         next_scan->component_index[j] = c + j;
    145       }
    146       ++next_scan;
    147     }
    148   }
    149   JXL_ASSERT(next_scan - cinfo->script_space == cinfo->script_space_size);
    150   cinfo->scan_info = cinfo->script_space;
    151   cinfo->num_scans = cinfo->script_space_size;
    152 }
    153 
    154 void ValidateScanScript(j_compress_ptr cinfo) {
    155   // Mask of coefficient bits defined by the scan script, for each component
    156   // and coefficient index.
    157   uint16_t comp_mask[kMaxComponents][DCTSIZE2] = {};
    158   static constexpr int kMaxRefinementBit = 10;
    159 
    160   for (int i = 0; i < cinfo->num_scans; ++i) {
    161     const jpeg_scan_info& si = cinfo->scan_info[i];
    162     if (si.comps_in_scan < 1 || si.comps_in_scan > MAX_COMPS_IN_SCAN) {
    163       JPEGLI_ERROR("Invalid number of components in scan %d", si.comps_in_scan);
    164     }
    165     int last_ci = -1;
    166     for (int j = 0; j < si.comps_in_scan; ++j) {
    167       int ci = si.component_index[j];
    168       if (ci < 0 || ci >= cinfo->num_components) {
    169         JPEGLI_ERROR("Invalid component index %d in scan", ci);
    170       } else if (ci == last_ci) {
    171         JPEGLI_ERROR("Duplicate component index %d in scan", ci);
    172       } else if (ci < last_ci) {
    173         JPEGLI_ERROR("Out of order component index %d in scan", ci);
    174       }
    175       last_ci = ci;
    176     }
    177     if (si.Ss < 0 || si.Se < si.Ss || si.Se >= DCTSIZE2) {
    178       JPEGLI_ERROR("Invalid spectral range %d .. %d in scan", si.Ss, si.Se);
    179     }
    180     if (si.Ah < 0 || si.Al < 0 || si.Al > kMaxRefinementBit) {
    181       JPEGLI_ERROR("Invalid refinement bits %d/%d", si.Ah, si.Al);
    182     }
    183     if (!cinfo->progressive_mode) {
    184       if (si.Ss != 0 || si.Se != DCTSIZE2 - 1 || si.Ah != 0 || si.Al != 0) {
    185         JPEGLI_ERROR("Invalid scan for sequential mode");
    186       }
    187     } else {
    188       if (si.Ss == 0 && si.Se != 0) {
    189         JPEGLI_ERROR("DC and AC together in progressive scan");
    190       }
    191     }
    192     if (si.Ss != 0 && si.comps_in_scan != 1) {
    193       JPEGLI_ERROR("Interleaved AC only scan.");
    194     }
    195     for (int j = 0; j < si.comps_in_scan; ++j) {
    196       int ci = si.component_index[j];
    197       if (si.Ss != 0 && comp_mask[ci][0] == 0) {
    198         JPEGLI_ERROR("AC before DC in component %d of scan", ci);
    199       }
    200       for (int k = si.Ss; k <= si.Se; ++k) {
    201         if (comp_mask[ci][k] == 0) {
    202           if (si.Ah != 0) {
    203             JPEGLI_ERROR("Invalid first scan refinement bit");
    204           }
    205           comp_mask[ci][k] = ((0xffff << si.Al) & 0xffff);
    206         } else {
    207           if (comp_mask[ci][k] != ((0xffff << si.Ah) & 0xffff) ||
    208               si.Al != si.Ah - 1) {
    209             JPEGLI_ERROR("Invalid refinement bit progression.");
    210           }
    211           comp_mask[ci][k] |= 1 << si.Al;
    212         }
    213       }
    214     }
    215     if (si.comps_in_scan > 1) {
    216       size_t mcu_size = 0;
    217       for (int j = 0; j < si.comps_in_scan; ++j) {
    218         int ci = si.component_index[j];
    219         jpeg_component_info* comp = &cinfo->comp_info[ci];
    220         mcu_size += comp->h_samp_factor * comp->v_samp_factor;
    221       }
    222       if (mcu_size > C_MAX_BLOCKS_IN_MCU) {
    223         JPEGLI_ERROR("MCU size too big");
    224       }
    225     }
    226   }
    227   for (int c = 0; c < cinfo->num_components; ++c) {
    228     for (int k = 0; k < DCTSIZE2; ++k) {
    229       if (comp_mask[c][k] != 0xffff) {
    230         JPEGLI_ERROR("Incomplete scan of component %d and frequency %d", c, k);
    231       }
    232     }
    233   }
    234 }
    235 
    236 void ProcessCompressionParams(j_compress_ptr cinfo) {
    237   if (cinfo->dest == nullptr) {
    238     JPEGLI_ERROR("Missing destination.");
    239   }
    240   if (cinfo->image_width < 1 || cinfo->image_height < 1 ||
    241       cinfo->input_components < 1) {
    242     JPEGLI_ERROR("Empty input image.");
    243   }
    244   if (cinfo->image_width > static_cast<int>(JPEG_MAX_DIMENSION) ||
    245       cinfo->image_height > static_cast<int>(JPEG_MAX_DIMENSION) ||
    246       cinfo->input_components > static_cast<int>(kMaxComponents)) {
    247     JPEGLI_ERROR("Input image too big.");
    248   }
    249   if (cinfo->num_components < 1 ||
    250       cinfo->num_components > static_cast<int>(kMaxComponents)) {
    251     JPEGLI_ERROR("Invalid number of components.");
    252   }
    253   if (cinfo->data_precision != kJpegPrecision) {
    254     JPEGLI_ERROR("Invalid data precision");
    255   }
    256   if (cinfo->arith_code) {
    257     JPEGLI_ERROR("Arithmetic coding is not implemented.");
    258   }
    259   if (cinfo->CCIR601_sampling) {
    260     JPEGLI_ERROR("CCIR601 sampling is not implemented.");
    261   }
    262   if (cinfo->restart_interval > 65535u) {
    263     JPEGLI_ERROR("Restart interval too big");
    264   }
    265   if (cinfo->smoothing_factor < 0 || cinfo->smoothing_factor > 100) {
    266     JPEGLI_ERROR("Invalid smoothing factor %d", cinfo->smoothing_factor);
    267   }
    268   jpeg_comp_master* m = cinfo->master;
    269   cinfo->max_h_samp_factor = cinfo->max_v_samp_factor = 1;
    270   for (int c = 0; c < cinfo->num_components; ++c) {
    271     jpeg_component_info* comp = &cinfo->comp_info[c];
    272     if (comp->component_index != c) {
    273       JPEGLI_ERROR("Invalid component index");
    274     }
    275     for (int j = 0; j < c; ++j) {
    276       if (cinfo->comp_info[j].component_id == comp->component_id) {
    277         JPEGLI_ERROR("Duplicate component id %d", comp->component_id);
    278       }
    279     }
    280     if (comp->h_samp_factor <= 0 || comp->v_samp_factor <= 0 ||
    281         comp->h_samp_factor > MAX_SAMP_FACTOR ||
    282         comp->v_samp_factor > MAX_SAMP_FACTOR) {
    283       JPEGLI_ERROR("Invalid sampling factor %d x %d", comp->h_samp_factor,
    284                    comp->v_samp_factor);
    285     }
    286     cinfo->max_h_samp_factor =
    287         std::max(comp->h_samp_factor, cinfo->max_h_samp_factor);
    288     cinfo->max_v_samp_factor =
    289         std::max(comp->v_samp_factor, cinfo->max_v_samp_factor);
    290   }
    291   if (cinfo->num_components == 1 &&
    292       (cinfo->max_h_samp_factor != 1 || cinfo->max_v_samp_factor != 1)) {
    293     JPEGLI_ERROR("Sampling is not supported for simgle component image.");
    294   }
    295   size_t iMCU_width = DCTSIZE * cinfo->max_h_samp_factor;
    296   size_t iMCU_height = DCTSIZE * cinfo->max_v_samp_factor;
    297   size_t total_iMCU_cols = DivCeil(cinfo->image_width, iMCU_width);
    298   cinfo->total_iMCU_rows = DivCeil(cinfo->image_height, iMCU_height);
    299   m->xsize_blocks = total_iMCU_cols * cinfo->max_h_samp_factor;
    300   m->ysize_blocks = cinfo->total_iMCU_rows * cinfo->max_v_samp_factor;
    301 
    302   size_t blocks_per_iMCU = 0;
    303   for (int c = 0; c < cinfo->num_components; ++c) {
    304     jpeg_component_info* comp = &cinfo->comp_info[c];
    305     if (cinfo->max_h_samp_factor % comp->h_samp_factor != 0 ||
    306         cinfo->max_v_samp_factor % comp->v_samp_factor != 0) {
    307       JPEGLI_ERROR("Non-integral sampling ratios are not supported.");
    308     }
    309     m->h_factor[c] = cinfo->max_h_samp_factor / comp->h_samp_factor;
    310     m->v_factor[c] = cinfo->max_v_samp_factor / comp->v_samp_factor;
    311     comp->downsampled_width = DivCeil(cinfo->image_width, m->h_factor[c]);
    312     comp->downsampled_height = DivCeil(cinfo->image_height, m->v_factor[c]);
    313     comp->width_in_blocks = DivCeil(comp->downsampled_width, DCTSIZE);
    314     comp->height_in_blocks = DivCeil(comp->downsampled_height, DCTSIZE);
    315     blocks_per_iMCU += comp->h_samp_factor * comp->v_samp_factor;
    316   }
    317   m->blocks_per_iMCU_row = total_iMCU_cols * blocks_per_iMCU;
    318   // Disable adaptive quantization for subsampled luma channel.
    319   int y_channel = cinfo->jpeg_color_space == JCS_RGB ? 1 : 0;
    320   jpeg_component_info* y_comp = &cinfo->comp_info[y_channel];
    321   if (y_comp->h_samp_factor != cinfo->max_h_samp_factor ||
    322       y_comp->v_samp_factor != cinfo->max_v_samp_factor) {
    323     m->use_adaptive_quantization = false;
    324   }
    325   if (cinfo->scan_info == nullptr) {
    326     SetDefaultScanScript(cinfo);
    327   }
    328   cinfo->progressive_mode = TO_JXL_BOOL(cinfo->scan_info->Ss != 0 ||
    329                                         cinfo->scan_info->Se != DCTSIZE2 - 1);
    330   ValidateScanScript(cinfo);
    331   m->scan_token_info =
    332       Allocate<ScanTokenInfo>(cinfo, cinfo->num_scans, JPOOL_IMAGE);
    333   memset(m->scan_token_info, 0, cinfo->num_scans * sizeof(ScanTokenInfo));
    334   m->ac_ctx_offset = Allocate<uint8_t>(cinfo, cinfo->num_scans, JPOOL_IMAGE);
    335   size_t num_ac_contexts = 0;
    336   for (int i = 0; i < cinfo->num_scans; ++i) {
    337     const jpeg_scan_info* scan_info = &cinfo->scan_info[i];
    338     m->ac_ctx_offset[i] = 4 + num_ac_contexts;
    339     if (scan_info->Se > 0) {
    340       num_ac_contexts += scan_info->comps_in_scan;
    341     }
    342     if (num_ac_contexts > 252) {
    343       JPEGLI_ERROR("Too many AC scans in image");
    344     }
    345     ScanTokenInfo* sti = &m->scan_token_info[i];
    346     if (scan_info->comps_in_scan == 1) {
    347       int comp_idx = scan_info->component_index[0];
    348       jpeg_component_info* comp = &cinfo->comp_info[comp_idx];
    349       sti->MCUs_per_row = comp->width_in_blocks;
    350       sti->MCU_rows_in_scan = comp->height_in_blocks;
    351       sti->blocks_in_MCU = 1;
    352     } else {
    353       sti->MCUs_per_row =
    354           DivCeil(cinfo->image_width, DCTSIZE * cinfo->max_h_samp_factor);
    355       sti->MCU_rows_in_scan =
    356           DivCeil(cinfo->image_height, DCTSIZE * cinfo->max_v_samp_factor);
    357       sti->blocks_in_MCU = 0;
    358       for (int j = 0; j < scan_info->comps_in_scan; ++j) {
    359         int comp_idx = scan_info->component_index[j];
    360         jpeg_component_info* comp = &cinfo->comp_info[comp_idx];
    361         sti->blocks_in_MCU += comp->h_samp_factor * comp->v_samp_factor;
    362       }
    363     }
    364     size_t num_MCUs = sti->MCU_rows_in_scan * sti->MCUs_per_row;
    365     sti->num_blocks = num_MCUs * sti->blocks_in_MCU;
    366     if (cinfo->restart_in_rows <= 0) {
    367       sti->restart_interval = cinfo->restart_interval;
    368     } else {
    369       sti->restart_interval =
    370           std::min<size_t>(sti->MCUs_per_row * cinfo->restart_in_rows, 65535u);
    371     }
    372     sti->num_restarts = sti->restart_interval > 0
    373                             ? DivCeil(num_MCUs, sti->restart_interval)
    374                             : 1;
    375     sti->restarts = Allocate<size_t>(cinfo, sti->num_restarts, JPOOL_IMAGE);
    376   }
    377   m->num_contexts = 4 + num_ac_contexts;
    378 }
    379 
    380 bool IsStreamingSupported(j_compress_ptr cinfo) {
    381   if (cinfo->global_state == kEncWriteCoeffs) {
    382     return false;
    383   }
    384   // TODO(szabadka) Remove this restriction.
    385   if (cinfo->restart_interval > 0 || cinfo->restart_in_rows > 0) {
    386     return false;
    387   }
    388   if (cinfo->num_scans > 1) {
    389     return false;
    390   }
    391   if (cinfo->master->psnr_target > 0) {
    392     return false;
    393   }
    394   return true;
    395 }
    396 
    397 void AllocateBuffers(j_compress_ptr cinfo) {
    398   jpeg_comp_master* m = cinfo->master;
    399   memset(m->last_dc_coeff, 0, sizeof(m->last_dc_coeff));
    400   if (!IsStreamingSupported(cinfo) || cinfo->optimize_coding) {
    401     int ysize_blocks = DivCeil(cinfo->image_height, DCTSIZE);
    402     int num_arrays = cinfo->num_scans * ysize_blocks;
    403     m->token_arrays = Allocate<TokenArray>(cinfo, num_arrays, JPOOL_IMAGE);
    404     m->cur_token_array = 0;
    405     memset(m->token_arrays, 0, num_arrays * sizeof(TokenArray));
    406     m->num_tokens = 0;
    407     m->total_num_tokens = 0;
    408   }
    409   if (cinfo->global_state == kEncWriteCoeffs) {
    410     return;
    411   }
    412   size_t iMCU_width = DCTSIZE * cinfo->max_h_samp_factor;
    413   size_t iMCU_height = DCTSIZE * cinfo->max_v_samp_factor;
    414   size_t total_iMCU_cols = DivCeil(cinfo->image_width, iMCU_width);
    415   size_t xsize_full = total_iMCU_cols * iMCU_width;
    416   size_t ysize_full = 3 * iMCU_height;
    417   if (!cinfo->raw_data_in) {
    418     int num_all_components =
    419         std::max(cinfo->input_components, cinfo->num_components);
    420     for (int c = 0; c < num_all_components; ++c) {
    421       m->input_buffer[c].Allocate(cinfo, ysize_full, xsize_full);
    422     }
    423   }
    424   for (int c = 0; c < cinfo->num_components; ++c) {
    425     jpeg_component_info* comp = &cinfo->comp_info[c];
    426     size_t xsize = total_iMCU_cols * comp->h_samp_factor * DCTSIZE;
    427     size_t ysize = 3 * comp->v_samp_factor * DCTSIZE;
    428     if (cinfo->raw_data_in) {
    429       m->input_buffer[c].Allocate(cinfo, ysize, xsize);
    430     }
    431     m->smooth_input[c] = &m->input_buffer[c];
    432     if (!cinfo->raw_data_in && cinfo->smoothing_factor) {
    433       m->smooth_input[c] = Allocate<RowBuffer<float>>(cinfo, 1, JPOOL_IMAGE);
    434       m->smooth_input[c]->Allocate(cinfo, ysize_full, xsize_full);
    435     }
    436     m->raw_data[c] = m->smooth_input[c];
    437     if (!cinfo->raw_data_in && (m->h_factor[c] > 1 || m->v_factor[c] > 1)) {
    438       m->raw_data[c] = Allocate<RowBuffer<float>>(cinfo, 1, JPOOL_IMAGE);
    439       m->raw_data[c]->Allocate(cinfo, ysize, xsize);
    440     }
    441     m->quant_mul[c] = Allocate<float>(cinfo, DCTSIZE2, JPOOL_IMAGE_ALIGNED);
    442   }
    443   m->dct_buffer = Allocate<float>(cinfo, 2 * DCTSIZE2, JPOOL_IMAGE_ALIGNED);
    444   m->block_tmp = Allocate<int32_t>(cinfo, DCTSIZE2 * 4, JPOOL_IMAGE_ALIGNED);
    445   if (!IsStreamingSupported(cinfo)) {
    446     m->coeff_buffers =
    447         Allocate<jvirt_barray_ptr>(cinfo, cinfo->num_components, JPOOL_IMAGE);
    448     for (int c = 0; c < cinfo->num_components; ++c) {
    449       jpeg_component_info* comp = &cinfo->comp_info[c];
    450       const size_t xsize_blocks = comp->width_in_blocks;
    451       const size_t ysize_blocks = comp->height_in_blocks;
    452       m->coeff_buffers[c] = (*cinfo->mem->request_virt_barray)(
    453           reinterpret_cast<j_common_ptr>(cinfo), JPOOL_IMAGE,
    454           /*pre_zero=*/FALSE, xsize_blocks, ysize_blocks, comp->v_samp_factor);
    455     }
    456   }
    457   if (m->use_adaptive_quantization) {
    458     int y_channel = cinfo->jpeg_color_space == JCS_RGB ? 1 : 0;
    459     jpeg_component_info* y_comp = &cinfo->comp_info[y_channel];
    460     const size_t xsize_blocks = y_comp->width_in_blocks;
    461     const size_t vecsize = VectorSize();
    462     const size_t xsize_padded = DivCeil(2 * xsize_blocks, vecsize) * vecsize;
    463     m->diff_buffer =
    464         Allocate<float>(cinfo, xsize_blocks * DCTSIZE + 8, JPOOL_IMAGE_ALIGNED);
    465     m->fuzzy_erosion_tmp.Allocate(cinfo, 2, xsize_padded);
    466     m->pre_erosion.Allocate(cinfo, 6 * cinfo->max_v_samp_factor, xsize_padded);
    467     size_t qf_height = cinfo->max_v_samp_factor;
    468     if (m->psnr_target > 0) {
    469       qf_height *= cinfo->total_iMCU_rows;
    470     }
    471     m->quant_field.Allocate(cinfo, qf_height, xsize_blocks);
    472   } else {
    473     m->quant_field.Allocate(cinfo, 1, m->xsize_blocks);
    474     m->quant_field.FillRow(0, 0, m->xsize_blocks);
    475   }
    476   for (int c = 0; c < cinfo->num_components; ++c) {
    477     m->zero_bias_offset[c] =
    478         Allocate<float>(cinfo, DCTSIZE2, JPOOL_IMAGE_ALIGNED);
    479     m->zero_bias_mul[c] = Allocate<float>(cinfo, DCTSIZE2, JPOOL_IMAGE_ALIGNED);
    480     memset(m->zero_bias_mul[c], 0, DCTSIZE2 * sizeof(float));
    481     memset(m->zero_bias_offset[c], 0, DCTSIZE2 * sizeof(float));
    482   }
    483 }
    484 
    485 void InitProgressMonitor(j_compress_ptr cinfo) {
    486   if (cinfo->progress == nullptr) {
    487     return;
    488   }
    489   if (IsStreamingSupported(cinfo)) {
    490     // We have only one input pass.
    491     cinfo->progress->total_passes = 1;
    492   } else {
    493     // We have one input pass, a histogram pass for each scan, and an encode
    494     // pass for each scan.
    495     cinfo->progress->total_passes = 1 + 2 * cinfo->num_scans;
    496   }
    497 }
    498 
    499 // Common setup code between streaming and transcoding code paths. Called in
    500 // both jpegli_start_compress() and jpegli_write_coefficients().
    501 void InitCompress(j_compress_ptr cinfo, boolean write_all_tables) {
    502   jpeg_comp_master* m = cinfo->master;
    503   (*cinfo->err->reset_error_mgr)(reinterpret_cast<j_common_ptr>(cinfo));
    504   ProcessCompressionParams(cinfo);
    505   InitProgressMonitor(cinfo);
    506   AllocateBuffers(cinfo);
    507   if (cinfo->global_state != kEncWriteCoeffs) {
    508     ChooseInputMethod(cinfo);
    509     if (!cinfo->raw_data_in) {
    510       ChooseColorTransform(cinfo);
    511       ChooseDownsampleMethods(cinfo);
    512     }
    513     QuantPass pass = m->psnr_target > 0 ? QuantPass::SEARCH_FIRST_PASS
    514                                         : QuantPass::NO_SEARCH;
    515     InitQuantizer(cinfo, pass);
    516   }
    517   if (write_all_tables) {
    518     jpegli_suppress_tables(cinfo, FALSE);
    519   }
    520   if (!cinfo->optimize_coding && !cinfo->progressive_mode) {
    521     CopyHuffmanTables(cinfo);
    522     InitEntropyCoder(cinfo);
    523   }
    524   (*cinfo->dest->init_destination)(cinfo);
    525   WriteFileHeader(cinfo);
    526   JpegBitWriterInit(cinfo);
    527   m->next_iMCU_row = 0;
    528   m->last_restart_interval = 0;
    529   m->next_dht_index = 0;
    530 }
    531 
    532 //
    533 // Input streaming
    534 //
    535 
    536 void ProgressMonitorInputPass(j_compress_ptr cinfo) {
    537   if (cinfo->progress == nullptr) {
    538     return;
    539   }
    540   cinfo->progress->completed_passes = 0;
    541   cinfo->progress->pass_counter = cinfo->next_scanline;
    542   cinfo->progress->pass_limit = cinfo->image_height;
    543   (*cinfo->progress->progress_monitor)(reinterpret_cast<j_common_ptr>(cinfo));
    544 }
    545 
    546 void ReadInputRow(j_compress_ptr cinfo, const uint8_t* scanline,
    547                   float* row[kMaxComponents]) {
    548   jpeg_comp_master* m = cinfo->master;
    549   int num_all_components =
    550       std::max(cinfo->input_components, cinfo->num_components);
    551   for (int c = 0; c < num_all_components; ++c) {
    552     row[c] = m->input_buffer[c].Row(m->next_input_row);
    553   }
    554   ++m->next_input_row;
    555   if (scanline == nullptr) {
    556     for (int c = 0; c < cinfo->input_components; ++c) {
    557       memset(row[c], 0, cinfo->image_width * sizeof(row[c][0]));
    558     }
    559     return;
    560   }
    561   (*m->input_method)(scanline, cinfo->image_width, row);
    562 }
    563 
    564 void PadInputBuffer(j_compress_ptr cinfo, float* row[kMaxComponents]) {
    565   jpeg_comp_master* m = cinfo->master;
    566   const size_t len0 = cinfo->image_width;
    567   const size_t len1 = m->xsize_blocks * DCTSIZE;
    568   for (int c = 0; c < cinfo->num_components; ++c) {
    569     // Pad row to a multiple of the iMCU width, plus create a border of 1
    570     // repeated pixel for adaptive quant field calculation.
    571     float last_val = row[c][len0 - 1];
    572     for (size_t x = len0; x <= len1; ++x) {
    573       row[c][x] = last_val;
    574     }
    575     row[c][-1] = row[c][0];
    576   }
    577   if (m->next_input_row == cinfo->image_height) {
    578     size_t num_rows = m->ysize_blocks * DCTSIZE - cinfo->image_height;
    579     for (size_t i = 0; i < num_rows; ++i) {
    580       for (int c = 0; c < cinfo->num_components; ++c) {
    581         float* dest = m->input_buffer[c].Row(m->next_input_row) - 1;
    582         memcpy(dest, row[c] - 1, (len1 + 2) * sizeof(dest[0]));
    583       }
    584       ++m->next_input_row;
    585     }
    586   }
    587 }
    588 
    589 void ProcessiMCURow(j_compress_ptr cinfo) {
    590   JXL_ASSERT(cinfo->master->next_iMCU_row < cinfo->total_iMCU_rows);
    591   if (!cinfo->raw_data_in) {
    592     ApplyInputSmoothing(cinfo);
    593     DownsampleInputBuffer(cinfo);
    594   }
    595   ComputeAdaptiveQuantField(cinfo);
    596   if (IsStreamingSupported(cinfo)) {
    597     if (cinfo->optimize_coding) {
    598       ComputeTokensForiMCURow(cinfo);
    599     } else {
    600       WriteiMCURow(cinfo);
    601     }
    602   } else {
    603     ComputeCoefficientsForiMCURow(cinfo);
    604   }
    605   ++cinfo->master->next_iMCU_row;
    606 }
    607 
    608 void ProcessiMCURows(j_compress_ptr cinfo) {
    609   jpeg_comp_master* m = cinfo->master;
    610   size_t iMCU_height = DCTSIZE * cinfo->max_v_samp_factor;
    611   // To have context rows both above and below the current iMCU row, we delay
    612   // processing the first iMCU row and process two iMCU rows after we receive
    613   // the last input row.
    614   if (m->next_input_row % iMCU_height == 0 && m->next_input_row > iMCU_height) {
    615     ProcessiMCURow(cinfo);
    616   }
    617   if (m->next_input_row >= cinfo->image_height) {
    618     ProcessiMCURow(cinfo);
    619   }
    620 }
    621 
    622 //
    623 // Non-streaming part
    624 //
    625 
    626 void ZigZagShuffleBlocks(j_compress_ptr cinfo) {
    627   JCOEF tmp[DCTSIZE2];
    628   for (int c = 0; c < cinfo->num_components; ++c) {
    629     jpeg_component_info* comp = &cinfo->comp_info[c];
    630     for (JDIMENSION by = 0; by < comp->height_in_blocks; ++by) {
    631       JBLOCKARRAY ba = GetBlockRow(cinfo, c, by);
    632       for (JDIMENSION bx = 0; bx < comp->width_in_blocks; ++bx) {
    633         JCOEF* block = &ba[0][bx][0];
    634         for (int k = 0; k < DCTSIZE2; ++k) {
    635           tmp[k] = block[kJPEGNaturalOrder[k]];
    636         }
    637         memcpy(block, tmp, sizeof(tmp));
    638       }
    639     }
    640   }
    641 }
    642 
    643 }  // namespace jpegli
    644 
    645 //
    646 // Parameter setup
    647 //
    648 
    649 void jpegli_CreateCompress(j_compress_ptr cinfo, int version,
    650                            size_t structsize) {
    651   cinfo->mem = nullptr;
    652   if (structsize != sizeof(*cinfo)) {
    653     JPEGLI_ERROR("jpegli_compress_struct has wrong size.");
    654   }
    655   jpegli::InitMemoryManager(reinterpret_cast<j_common_ptr>(cinfo));
    656   cinfo->progress = nullptr;
    657   cinfo->is_decompressor = FALSE;
    658   cinfo->global_state = jpegli::kEncStart;
    659   cinfo->dest = nullptr;
    660   cinfo->image_width = 0;
    661   cinfo->image_height = 0;
    662   cinfo->input_components = 0;
    663   cinfo->in_color_space = JCS_UNKNOWN;
    664   cinfo->input_gamma = 1.0f;
    665   cinfo->num_components = 0;
    666   cinfo->jpeg_color_space = JCS_UNKNOWN;
    667   cinfo->comp_info = nullptr;
    668   for (auto& quant_tbl_ptr : cinfo->quant_tbl_ptrs) {
    669     quant_tbl_ptr = nullptr;
    670   }
    671   for (int i = 0; i < NUM_HUFF_TBLS; ++i) {
    672     cinfo->dc_huff_tbl_ptrs[i] = nullptr;
    673     cinfo->ac_huff_tbl_ptrs[i] = nullptr;
    674   }
    675   memset(cinfo->arith_dc_L, 0, sizeof(cinfo->arith_dc_L));
    676   memset(cinfo->arith_dc_U, 0, sizeof(cinfo->arith_dc_U));
    677   memset(cinfo->arith_ac_K, 0, sizeof(cinfo->arith_ac_K));
    678   cinfo->write_Adobe_marker = FALSE;
    679   cinfo->master = jpegli::Allocate<jpeg_comp_master>(cinfo, 1);
    680   jpegli::InitializeCompressParams(cinfo);
    681   cinfo->master->force_baseline = true;
    682   cinfo->master->xyb_mode = false;
    683   cinfo->master->cicp_transfer_function = 2;  // unknown transfer function code
    684   cinfo->master->use_std_tables = false;
    685   cinfo->master->use_adaptive_quantization = true;
    686   cinfo->master->progressive_level = jpegli::kDefaultProgressiveLevel;
    687   cinfo->master->data_type = JPEGLI_TYPE_UINT8;
    688   cinfo->master->endianness = JPEGLI_NATIVE_ENDIAN;
    689   cinfo->master->coeff_buffers = nullptr;
    690 }
    691 
    692 void jpegli_set_xyb_mode(j_compress_ptr cinfo) {
    693   CheckState(cinfo, jpegli::kEncStart);
    694   cinfo->master->xyb_mode = true;
    695 }
    696 
    697 void jpegli_set_cicp_transfer_function(j_compress_ptr cinfo, int code) {
    698   CheckState(cinfo, jpegli::kEncStart);
    699   cinfo->master->cicp_transfer_function = code;
    700 }
    701 
    702 void jpegli_set_defaults(j_compress_ptr cinfo) {
    703   CheckState(cinfo, jpegli::kEncStart);
    704   jpegli::InitializeCompressParams(cinfo);
    705   jpegli_default_colorspace(cinfo);
    706   jpegli_set_quality(cinfo, 90, TRUE);
    707   jpegli_set_progressive_level(cinfo, jpegli::kDefaultProgressiveLevel);
    708   jpegli::AddStandardHuffmanTables(reinterpret_cast<j_common_ptr>(cinfo),
    709                                    /*is_dc=*/false);
    710   jpegli::AddStandardHuffmanTables(reinterpret_cast<j_common_ptr>(cinfo),
    711                                    /*is_dc=*/true);
    712 }
    713 
    714 void jpegli_default_colorspace(j_compress_ptr cinfo) {
    715   CheckState(cinfo, jpegli::kEncStart);
    716   switch (cinfo->in_color_space) {
    717     case JCS_GRAYSCALE:
    718       jpegli_set_colorspace(cinfo, JCS_GRAYSCALE);
    719       break;
    720     case JCS_RGB: {
    721       if (cinfo->master->xyb_mode) {
    722         jpegli_set_colorspace(cinfo, JCS_RGB);
    723       } else {
    724         jpegli_set_colorspace(cinfo, JCS_YCbCr);
    725       }
    726       break;
    727     }
    728     case JCS_YCbCr:
    729       jpegli_set_colorspace(cinfo, JCS_YCbCr);
    730       break;
    731     case JCS_CMYK:
    732       jpegli_set_colorspace(cinfo, JCS_CMYK);
    733       break;
    734     case JCS_YCCK:
    735       jpegli_set_colorspace(cinfo, JCS_YCCK);
    736       break;
    737     case JCS_UNKNOWN:
    738       jpegli_set_colorspace(cinfo, JCS_UNKNOWN);
    739       break;
    740     default:
    741       JPEGLI_ERROR("Unsupported input colorspace %d", cinfo->in_color_space);
    742   }
    743 }
    744 
    745 void jpegli_set_colorspace(j_compress_ptr cinfo, J_COLOR_SPACE colorspace) {
    746   CheckState(cinfo, jpegli::kEncStart);
    747   cinfo->jpeg_color_space = colorspace;
    748   switch (colorspace) {
    749     case JCS_GRAYSCALE:
    750       cinfo->num_components = 1;
    751       break;
    752     case JCS_RGB:
    753     case JCS_YCbCr:
    754       cinfo->num_components = 3;
    755       break;
    756     case JCS_CMYK:
    757     case JCS_YCCK:
    758       cinfo->num_components = 4;
    759       break;
    760     case JCS_UNKNOWN:
    761       cinfo->num_components =
    762           std::min<int>(jpegli::kMaxComponents, cinfo->input_components);
    763       break;
    764     default:
    765       JPEGLI_ERROR("Unsupported jpeg colorspace %d", colorspace);
    766   }
    767   // Adobe marker is only needed to distinguish CMYK and YCCK JPEGs.
    768   cinfo->write_Adobe_marker = TO_JXL_BOOL(cinfo->jpeg_color_space == JCS_YCCK);
    769   if (cinfo->comp_info == nullptr) {
    770     cinfo->comp_info =
    771         jpegli::Allocate<jpeg_component_info>(cinfo, MAX_COMPONENTS);
    772   }
    773   memset(cinfo->comp_info, 0,
    774          jpegli::kMaxComponents * sizeof(jpeg_component_info));
    775   for (int c = 0; c < cinfo->num_components; ++c) {
    776     jpeg_component_info* comp = &cinfo->comp_info[c];
    777     comp->component_index = c;
    778     comp->component_id = c + 1;
    779     comp->h_samp_factor = 1;
    780     comp->v_samp_factor = 1;
    781     comp->quant_tbl_no = 0;
    782     comp->dc_tbl_no = 0;
    783     comp->ac_tbl_no = 0;
    784   }
    785   if (colorspace == JCS_RGB) {
    786     cinfo->comp_info[0].component_id = 'R';
    787     cinfo->comp_info[1].component_id = 'G';
    788     cinfo->comp_info[2].component_id = 'B';
    789     if (cinfo->master->xyb_mode) {
    790       // Subsample blue channel.
    791       cinfo->comp_info[0].h_samp_factor = cinfo->comp_info[0].v_samp_factor = 2;
    792       cinfo->comp_info[1].h_samp_factor = cinfo->comp_info[1].v_samp_factor = 2;
    793       cinfo->comp_info[2].h_samp_factor = cinfo->comp_info[2].v_samp_factor = 1;
    794       // Use separate quantization tables for each component
    795       cinfo->comp_info[1].quant_tbl_no = 1;
    796       cinfo->comp_info[2].quant_tbl_no = 2;
    797     }
    798   } else if (colorspace == JCS_CMYK) {
    799     cinfo->comp_info[0].component_id = 'C';
    800     cinfo->comp_info[1].component_id = 'M';
    801     cinfo->comp_info[2].component_id = 'Y';
    802     cinfo->comp_info[3].component_id = 'K';
    803   } else if (colorspace == JCS_YCbCr || colorspace == JCS_YCCK) {
    804     // Use separate quantization and Huffman tables for luma and chroma
    805     cinfo->comp_info[1].quant_tbl_no = 1;
    806     cinfo->comp_info[2].quant_tbl_no = 1;
    807     cinfo->comp_info[1].dc_tbl_no = cinfo->comp_info[1].ac_tbl_no = 1;
    808     cinfo->comp_info[2].dc_tbl_no = cinfo->comp_info[2].ac_tbl_no = 1;
    809   }
    810 }
    811 
    812 void jpegli_set_distance(j_compress_ptr cinfo, float distance,
    813                          boolean force_baseline) {
    814   CheckState(cinfo, jpegli::kEncStart);
    815   cinfo->master->force_baseline = FROM_JXL_BOOL(force_baseline);
    816   float distances[NUM_QUANT_TBLS] = {distance, distance, distance};
    817   jpegli::SetQuantMatrices(cinfo, distances, /*add_two_chroma_tables=*/true);
    818 }
    819 
    820 float jpegli_quality_to_distance(int quality) {
    821   return (quality >= 100  ? 0.01f
    822           : quality >= 30 ? 0.1f + (100 - quality) * 0.09f
    823                           : 53.0f / 3000.0f * quality * quality -
    824                                 23.0f / 20.0f * quality + 25.0f);
    825 }
    826 
    827 void jpegli_set_psnr(j_compress_ptr cinfo, float psnr, float tolerance,
    828                      float min_distance, float max_distance) {
    829   CheckState(cinfo, jpegli::kEncStart);
    830   cinfo->master->psnr_target = psnr;
    831   cinfo->master->psnr_tolerance = tolerance;
    832   cinfo->master->min_distance = min_distance;
    833   cinfo->master->max_distance = max_distance;
    834 }
    835 
    836 void jpegli_set_quality(j_compress_ptr cinfo, int quality,
    837                         boolean force_baseline) {
    838   CheckState(cinfo, jpegli::kEncStart);
    839   cinfo->master->force_baseline = FROM_JXL_BOOL(force_baseline);
    840   float distance = jpegli_quality_to_distance(quality);
    841   float distances[NUM_QUANT_TBLS] = {distance, distance, distance};
    842   jpegli::SetQuantMatrices(cinfo, distances, /*add_two_chroma_tables=*/false);
    843 }
    844 
    845 void jpegli_set_linear_quality(j_compress_ptr cinfo, int scale_factor,
    846                                boolean force_baseline) {
    847   CheckState(cinfo, jpegli::kEncStart);
    848   cinfo->master->force_baseline = FROM_JXL_BOOL(force_baseline);
    849   float distance = jpegli::LinearQualityToDistance(scale_factor);
    850   float distances[NUM_QUANT_TBLS] = {distance, distance, distance};
    851   jpegli::SetQuantMatrices(cinfo, distances, /*add_two_chroma_tables=*/false);
    852 }
    853 
    854 #if JPEG_LIB_VERSION >= 70
    855 void jpegli_default_qtables(j_compress_ptr cinfo, boolean force_baseline) {
    856   CheckState(cinfo, jpegli::kEncStart);
    857   cinfo->master->force_baseline = force_baseline;
    858   float distances[NUM_QUANT_TBLS];
    859   for (int i = 0; i < NUM_QUANT_TBLS; ++i) {
    860     distances[i] = jpegli::LinearQualityToDistance(cinfo->q_scale_factor[i]);
    861   }
    862   jpegli::SetQuantMatrices(cinfo, distances, /*add_two_chroma_tables=*/false);
    863 }
    864 #endif
    865 
    866 int jpegli_quality_scaling(int quality) {
    867   quality = std::min(100, std::max(1, quality));
    868   return quality < 50 ? 5000 / quality : 200 - 2 * quality;
    869 }
    870 
    871 void jpegli_use_standard_quant_tables(j_compress_ptr cinfo) {
    872   CheckState(cinfo, jpegli::kEncStart);
    873   cinfo->master->use_std_tables = true;
    874 }
    875 
    876 void jpegli_add_quant_table(j_compress_ptr cinfo, int which_tbl,
    877                             const unsigned int* basic_table, int scale_factor,
    878                             boolean force_baseline) {
    879   CheckState(cinfo, jpegli::kEncStart);
    880   if (which_tbl < 0 || which_tbl > NUM_QUANT_TBLS) {
    881     JPEGLI_ERROR("Invalid quant table index %d", which_tbl);
    882   }
    883   if (cinfo->quant_tbl_ptrs[which_tbl] == nullptr) {
    884     cinfo->quant_tbl_ptrs[which_tbl] =
    885         jpegli_alloc_quant_table(reinterpret_cast<j_common_ptr>(cinfo));
    886   }
    887   int max_qval = force_baseline ? 255 : 32767U;
    888   JQUANT_TBL* quant_table = cinfo->quant_tbl_ptrs[which_tbl];
    889   for (int k = 0; k < DCTSIZE2; ++k) {
    890     int qval = (basic_table[k] * scale_factor + 50) / 100;
    891     qval = std::max(1, std::min(qval, max_qval));
    892     quant_table->quantval[k] = qval;
    893   }
    894   quant_table->sent_table = FALSE;
    895 }
    896 
    897 void jpegli_enable_adaptive_quantization(j_compress_ptr cinfo, boolean value) {
    898   CheckState(cinfo, jpegli::kEncStart);
    899   cinfo->master->use_adaptive_quantization = FROM_JXL_BOOL(value);
    900 }
    901 
    902 void jpegli_simple_progression(j_compress_ptr cinfo) {
    903   CheckState(cinfo, jpegli::kEncStart);
    904   jpegli_set_progressive_level(cinfo, 2);
    905 }
    906 
    907 void jpegli_set_progressive_level(j_compress_ptr cinfo, int level) {
    908   CheckState(cinfo, jpegli::kEncStart);
    909   if (level < 0) {
    910     JPEGLI_ERROR("Invalid progressive level %d", level);
    911   }
    912   cinfo->master->progressive_level = level;
    913 }
    914 
    915 void jpegli_set_input_format(j_compress_ptr cinfo, JpegliDataType data_type,
    916                              JpegliEndianness endianness) {
    917   CheckState(cinfo, jpegli::kEncStart);
    918   switch (data_type) {
    919     case JPEGLI_TYPE_UINT8:
    920     case JPEGLI_TYPE_UINT16:
    921     case JPEGLI_TYPE_FLOAT:
    922       cinfo->master->data_type = data_type;
    923       break;
    924     default:
    925       JPEGLI_ERROR("Unsupported data type %d", data_type);
    926   }
    927   switch (endianness) {
    928     case JPEGLI_NATIVE_ENDIAN:
    929     case JPEGLI_LITTLE_ENDIAN:
    930     case JPEGLI_BIG_ENDIAN:
    931       cinfo->master->endianness = endianness;
    932       break;
    933     default:
    934       JPEGLI_ERROR("Unsupported endianness %d", endianness);
    935   }
    936 }
    937 
    938 #if JPEG_LIB_VERSION >= 70
    939 void jpegli_calc_jpeg_dimensions(j_compress_ptr cinfo) {
    940   // Since input scaling is not supported, we just copy the image dimensions.
    941   cinfo->jpeg_width = cinfo->image_width;
    942   cinfo->jpeg_height = cinfo->image_height;
    943 }
    944 #endif
    945 
    946 void jpegli_copy_critical_parameters(j_decompress_ptr srcinfo,
    947                                      j_compress_ptr dstinfo) {
    948   CheckState(dstinfo, jpegli::kEncStart);
    949   // Image parameters.
    950   dstinfo->image_width = srcinfo->image_width;
    951   dstinfo->image_height = srcinfo->image_height;
    952   dstinfo->input_components = srcinfo->num_components;
    953   dstinfo->in_color_space = srcinfo->jpeg_color_space;
    954   dstinfo->input_gamma = srcinfo->output_gamma;
    955   // Compression parameters.
    956   jpegli_set_defaults(dstinfo);
    957   jpegli_set_colorspace(dstinfo, srcinfo->jpeg_color_space);
    958   if (dstinfo->num_components != srcinfo->num_components) {
    959     const auto& cinfo = dstinfo;
    960     return JPEGLI_ERROR("Mismatch between src colorspace and components");
    961   }
    962   dstinfo->data_precision = srcinfo->data_precision;
    963   dstinfo->CCIR601_sampling = srcinfo->CCIR601_sampling;
    964   dstinfo->JFIF_major_version = srcinfo->JFIF_major_version;
    965   dstinfo->JFIF_minor_version = srcinfo->JFIF_minor_version;
    966   dstinfo->density_unit = srcinfo->density_unit;
    967   dstinfo->X_density = srcinfo->X_density;
    968   dstinfo->Y_density = srcinfo->Y_density;
    969   for (int c = 0; c < dstinfo->num_components; ++c) {
    970     jpeg_component_info* srccomp = &srcinfo->comp_info[c];
    971     jpeg_component_info* dstcomp = &dstinfo->comp_info[c];
    972     dstcomp->component_id = srccomp->component_id;
    973     dstcomp->h_samp_factor = srccomp->h_samp_factor;
    974     dstcomp->v_samp_factor = srccomp->v_samp_factor;
    975     dstcomp->quant_tbl_no = srccomp->quant_tbl_no;
    976   }
    977   for (int i = 0; i < NUM_QUANT_TBLS; ++i) {
    978     if (!srcinfo->quant_tbl_ptrs[i]) continue;
    979     if (dstinfo->quant_tbl_ptrs[i] == nullptr) {
    980       dstinfo->quant_tbl_ptrs[i] = jpegli::Allocate<JQUANT_TBL>(dstinfo, 1);
    981     }
    982     memcpy(dstinfo->quant_tbl_ptrs[i], srcinfo->quant_tbl_ptrs[i],
    983            sizeof(JQUANT_TBL));
    984     dstinfo->quant_tbl_ptrs[i]->sent_table = FALSE;
    985   }
    986 }
    987 
    988 void jpegli_suppress_tables(j_compress_ptr cinfo, boolean suppress) {
    989   jpegli::SetSentTableFlag(cinfo->quant_tbl_ptrs, NUM_QUANT_TBLS, suppress);
    990   jpegli::SetSentTableFlag(cinfo->dc_huff_tbl_ptrs, NUM_HUFF_TBLS, suppress);
    991   jpegli::SetSentTableFlag(cinfo->ac_huff_tbl_ptrs, NUM_HUFF_TBLS, suppress);
    992 }
    993 
    994 //
    995 // Compressor initialization
    996 //
    997 
    998 void jpegli_start_compress(j_compress_ptr cinfo, boolean write_all_tables) {
    999   CheckState(cinfo, jpegli::kEncStart);
   1000   cinfo->global_state = jpegli::kEncHeader;
   1001   jpegli::InitCompress(cinfo, write_all_tables);
   1002   cinfo->next_scanline = 0;
   1003   cinfo->master->next_input_row = 0;
   1004 }
   1005 
   1006 void jpegli_write_coefficients(j_compress_ptr cinfo,
   1007                                jvirt_barray_ptr* coef_arrays) {
   1008   CheckState(cinfo, jpegli::kEncStart);
   1009   cinfo->global_state = jpegli::kEncWriteCoeffs;
   1010   jpegli::InitCompress(cinfo, /*write_all_tables=*/TRUE);
   1011   cinfo->master->coeff_buffers = coef_arrays;
   1012   cinfo->next_scanline = cinfo->image_height;
   1013   cinfo->master->next_input_row = cinfo->image_height;
   1014 }
   1015 
   1016 void jpegli_write_tables(j_compress_ptr cinfo) {
   1017   CheckState(cinfo, jpegli::kEncStart);
   1018   if (cinfo->dest == nullptr) {
   1019     JPEGLI_ERROR("Missing destination.");
   1020   }
   1021   jpeg_comp_master* m = cinfo->master;
   1022   (*cinfo->err->reset_error_mgr)(reinterpret_cast<j_common_ptr>(cinfo));
   1023   (*cinfo->dest->init_destination)(cinfo);
   1024   jpegli::WriteOutput(cinfo, {0xFF, 0xD8});  // SOI
   1025   jpegli::EncodeDQT(cinfo, /*write_all_tables=*/true);
   1026   jpegli::CopyHuffmanTables(cinfo);
   1027   jpegli::EncodeDHT(cinfo, 0, m->num_huffman_tables);
   1028   jpegli::WriteOutput(cinfo, {0xFF, 0xD9});  // EOI
   1029   (*cinfo->dest->term_destination)(cinfo);
   1030   jpegli_suppress_tables(cinfo, TRUE);
   1031 }
   1032 
   1033 //
   1034 // Marker writing
   1035 //
   1036 
   1037 void jpegli_write_m_header(j_compress_ptr cinfo, int marker,
   1038                            unsigned int datalen) {
   1039   CheckState(cinfo, jpegli::kEncHeader, jpegli::kEncWriteCoeffs);
   1040   if (datalen > jpegli::kMaxBytesInMarker) {
   1041     JPEGLI_ERROR("Invalid marker length %u", datalen);
   1042   }
   1043   if (marker != 0xfe && (marker < 0xe0 || marker > 0xef)) {
   1044     JPEGLI_ERROR(
   1045         "jpegli_write_m_header: Only APP and COM markers are supported.");
   1046   }
   1047   std::vector<uint8_t> marker_data(4 + datalen);
   1048   marker_data[0] = 0xff;
   1049   marker_data[1] = marker;
   1050   marker_data[2] = (datalen + 2) >> 8;
   1051   marker_data[3] = (datalen + 2) & 0xff;
   1052   jpegli::WriteOutput(cinfo, marker_data.data(), 4);
   1053 }
   1054 
   1055 void jpegli_write_m_byte(j_compress_ptr cinfo, int val) {
   1056   uint8_t data = val;
   1057   jpegli::WriteOutput(cinfo, &data, 1);
   1058 }
   1059 
   1060 void jpegli_write_marker(j_compress_ptr cinfo, int marker,
   1061                          const JOCTET* dataptr, unsigned int datalen) {
   1062   jpegli_write_m_header(cinfo, marker, datalen);
   1063   jpegli::WriteOutput(cinfo, dataptr, datalen);
   1064 }
   1065 
   1066 void jpegli_write_icc_profile(j_compress_ptr cinfo, const JOCTET* icc_data_ptr,
   1067                               unsigned int icc_data_len) {
   1068   constexpr size_t kMaxIccBytesInMarker =
   1069       jpegli::kMaxBytesInMarker - sizeof jpegli::kICCSignature - 2;
   1070   const int num_markers =
   1071       static_cast<int>(jpegli::DivCeil(icc_data_len, kMaxIccBytesInMarker));
   1072   size_t begin = 0;
   1073   for (int current_marker = 0; current_marker < num_markers; ++current_marker) {
   1074     const size_t length = std::min(kMaxIccBytesInMarker, icc_data_len - begin);
   1075     jpegli_write_m_header(
   1076         cinfo, jpegli::kICCMarker,
   1077         static_cast<unsigned int>(length + sizeof jpegli::kICCSignature + 2));
   1078     for (const unsigned char c : jpegli::kICCSignature) {
   1079       jpegli_write_m_byte(cinfo, c);
   1080     }
   1081     jpegli_write_m_byte(cinfo, current_marker + 1);
   1082     jpegli_write_m_byte(cinfo, num_markers);
   1083     for (size_t i = 0; i < length; ++i) {
   1084       jpegli_write_m_byte(cinfo, icc_data_ptr[begin]);
   1085       ++begin;
   1086     }
   1087   }
   1088 }
   1089 
   1090 //
   1091 // Input streaming
   1092 //
   1093 
   1094 JDIMENSION jpegli_write_scanlines(j_compress_ptr cinfo, JSAMPARRAY scanlines,
   1095                                   JDIMENSION num_lines) {
   1096   CheckState(cinfo, jpegli::kEncHeader, jpegli::kEncReadImage);
   1097   if (cinfo->raw_data_in) {
   1098     JPEGLI_ERROR("jpegli_write_raw_data() must be called for raw data mode.");
   1099   }
   1100   jpegli::ProgressMonitorInputPass(cinfo);
   1101   if (cinfo->global_state == jpegli::kEncHeader &&
   1102       jpegli::IsStreamingSupported(cinfo) && !cinfo->optimize_coding) {
   1103     jpegli::WriteFrameHeader(cinfo);
   1104     jpegli::WriteScanHeader(cinfo, 0);
   1105   }
   1106   cinfo->global_state = jpegli::kEncReadImage;
   1107   jpeg_comp_master* m = cinfo->master;
   1108   if (num_lines + cinfo->next_scanline > cinfo->image_height) {
   1109     num_lines = cinfo->image_height - cinfo->next_scanline;
   1110   }
   1111   JDIMENSION prev_scanline = cinfo->next_scanline;
   1112   size_t input_lag = (std::min<size_t>(cinfo->image_height, m->next_input_row) -
   1113                       cinfo->next_scanline);
   1114   if (input_lag > num_lines) {
   1115     JPEGLI_ERROR("Need at least %u lines to continue", input_lag);
   1116   }
   1117   if (input_lag > 0) {
   1118     if (!jpegli::EmptyBitWriterBuffer(&m->bw)) {
   1119       return 0;
   1120     }
   1121     cinfo->next_scanline += input_lag;
   1122   }
   1123   float* rows[jpegli::kMaxComponents];
   1124   for (size_t i = input_lag; i < num_lines; ++i) {
   1125     jpegli::ReadInputRow(cinfo, scanlines[i], rows);
   1126     (*m->color_transform)(rows, cinfo->image_width);
   1127     jpegli::PadInputBuffer(cinfo, rows);
   1128     jpegli::ProcessiMCURows(cinfo);
   1129     if (!jpegli::EmptyBitWriterBuffer(&m->bw)) {
   1130       break;
   1131     }
   1132     ++cinfo->next_scanline;
   1133   }
   1134   return cinfo->next_scanline - prev_scanline;
   1135 }
   1136 
   1137 JDIMENSION jpegli_write_raw_data(j_compress_ptr cinfo, JSAMPIMAGE data,
   1138                                  JDIMENSION num_lines) {
   1139   CheckState(cinfo, jpegli::kEncHeader, jpegli::kEncReadImage);
   1140   if (!cinfo->raw_data_in) {
   1141     JPEGLI_ERROR("jpegli_write_raw_data(): raw data mode was not set");
   1142   }
   1143   jpegli::ProgressMonitorInputPass(cinfo);
   1144   if (cinfo->global_state == jpegli::kEncHeader &&
   1145       jpegli::IsStreamingSupported(cinfo) && !cinfo->optimize_coding) {
   1146     jpegli::WriteFrameHeader(cinfo);
   1147     jpegli::WriteScanHeader(cinfo, 0);
   1148   }
   1149   cinfo->global_state = jpegli::kEncReadImage;
   1150   jpeg_comp_master* m = cinfo->master;
   1151   if (cinfo->next_scanline >= cinfo->image_height) {
   1152     return 0;
   1153   }
   1154   size_t iMCU_height = DCTSIZE * cinfo->max_v_samp_factor;
   1155   if (num_lines < iMCU_height) {
   1156     JPEGLI_ERROR("Missing input lines, minimum is %u", iMCU_height);
   1157   }
   1158   if (cinfo->next_scanline < m->next_input_row) {
   1159     JXL_ASSERT(m->next_input_row - cinfo->next_scanline == iMCU_height);
   1160     if (!jpegli::EmptyBitWriterBuffer(&m->bw)) {
   1161       return 0;
   1162     }
   1163     cinfo->next_scanline = m->next_input_row;
   1164     return iMCU_height;
   1165   }
   1166   size_t iMCU_y = m->next_input_row / iMCU_height;
   1167   float* rows[jpegli::kMaxComponents];
   1168   for (int c = 0; c < cinfo->num_components; ++c) {
   1169     JSAMPARRAY plane = data[c];
   1170     jpeg_component_info* comp = &cinfo->comp_info[c];
   1171     size_t xsize = comp->width_in_blocks * DCTSIZE;
   1172     size_t ysize = comp->v_samp_factor * DCTSIZE;
   1173     size_t y0 = iMCU_y * ysize;
   1174     auto& buffer = m->input_buffer[c];
   1175     for (size_t i = 0; i < ysize; ++i) {
   1176       rows[0] = buffer.Row(y0 + i);
   1177       if (plane[i] == nullptr) {
   1178         memset(rows[0], 0, xsize * sizeof(rows[0][0]));
   1179       } else {
   1180         (*m->input_method)(plane[i], xsize, rows);
   1181       }
   1182       // We need a border of 1 repeated pixel for adaptive quant field.
   1183       buffer.PadRow(y0 + i, xsize, /*border=*/1);
   1184     }
   1185   }
   1186   m->next_input_row += iMCU_height;
   1187   jpegli::ProcessiMCURows(cinfo);
   1188   if (!jpegli::EmptyBitWriterBuffer(&m->bw)) {
   1189     return 0;
   1190   }
   1191   cinfo->next_scanline += iMCU_height;
   1192   return iMCU_height;
   1193 }
   1194 
   1195 //
   1196 // Non-streaming part
   1197 //
   1198 
   1199 void jpegli_finish_compress(j_compress_ptr cinfo) {
   1200   CheckState(cinfo, jpegli::kEncReadImage, jpegli::kEncWriteCoeffs);
   1201   jpeg_comp_master* m = cinfo->master;
   1202   if (cinfo->next_scanline < cinfo->image_height) {
   1203     JPEGLI_ERROR("Incomplete image, expected %d rows, got %d",
   1204                  cinfo->image_height, cinfo->next_scanline);
   1205   }
   1206 
   1207   if (cinfo->global_state == jpegli::kEncWriteCoeffs) {
   1208     // Zig-zag shuffle all the blocks. For non-transcoding case it was already
   1209     // done in EncodeiMCURow().
   1210     jpegli::ZigZagShuffleBlocks(cinfo);
   1211   }
   1212 
   1213   if (m->psnr_target > 0) {
   1214     jpegli::QuantizetoPSNR(cinfo);
   1215   }
   1216 
   1217   const bool tokens_done = jpegli::IsStreamingSupported(cinfo);
   1218   const bool bitstream_done =
   1219       tokens_done && !FROM_JXL_BOOL(cinfo->optimize_coding);
   1220 
   1221   if (!tokens_done) {
   1222     jpegli::TokenizeJpeg(cinfo);
   1223   }
   1224 
   1225   if (cinfo->optimize_coding || cinfo->progressive_mode) {
   1226     jpegli::OptimizeHuffmanCodes(cinfo);
   1227     jpegli::InitEntropyCoder(cinfo);
   1228   }
   1229 
   1230   if (!bitstream_done) {
   1231     jpegli::WriteFrameHeader(cinfo);
   1232     for (int i = 0; i < cinfo->num_scans; ++i) {
   1233       jpegli::WriteScanHeader(cinfo, i);
   1234       jpegli::WriteScanData(cinfo, i);
   1235     }
   1236   } else {
   1237     JumpToByteBoundary(&m->bw);
   1238     if (!EmptyBitWriterBuffer(&m->bw)) {
   1239       JPEGLI_ERROR("Output suspension is not supported in finish_compress");
   1240     }
   1241   }
   1242 
   1243   jpegli::WriteOutput(cinfo, {0xFF, 0xD9});  // EOI
   1244   (*cinfo->dest->term_destination)(cinfo);
   1245 
   1246   // Release memory and reset global state.
   1247   jpegli_abort_compress(cinfo);
   1248 }
   1249 
   1250 void jpegli_abort_compress(j_compress_ptr cinfo) {
   1251   jpegli_abort(reinterpret_cast<j_common_ptr>(cinfo));
   1252 }
   1253 
   1254 void jpegli_destroy_compress(j_compress_ptr cinfo) {
   1255   jpegli_destroy(reinterpret_cast<j_common_ptr>(cinfo));
   1256 }