tor-browser

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

bitstream.cc (15335B)


      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/bitstream.h"
      7 
      8 #include <cmath>
      9 
     10 #include "lib/jpegli/bit_writer.h"
     11 #include "lib/jpegli/error.h"
     12 #include "lib/jpegli/memory_manager.h"
     13 
     14 namespace jpegli {
     15 
     16 void WriteOutput(j_compress_ptr cinfo, const uint8_t* buf, size_t bufsize) {
     17  size_t pos = 0;
     18  while (pos < bufsize) {
     19    if (cinfo->dest->free_in_buffer == 0 &&
     20        !(*cinfo->dest->empty_output_buffer)(cinfo)) {
     21      JPEGLI_ERROR("Destination suspension is not supported in markers.");
     22    }
     23    size_t len = std::min<size_t>(cinfo->dest->free_in_buffer, bufsize - pos);
     24    memcpy(cinfo->dest->next_output_byte, buf + pos, len);
     25    pos += len;
     26    cinfo->dest->free_in_buffer -= len;
     27    cinfo->dest->next_output_byte += len;
     28  }
     29 }
     30 
     31 void WriteOutput(j_compress_ptr cinfo, const std::vector<uint8_t>& bytes) {
     32  WriteOutput(cinfo, bytes.data(), bytes.size());
     33 }
     34 
     35 void WriteOutput(j_compress_ptr cinfo, std::initializer_list<uint8_t> bytes) {
     36  WriteOutput(cinfo, bytes.begin(), bytes.size());
     37 }
     38 
     39 void EncodeAPP0(j_compress_ptr cinfo) {
     40  WriteOutput(cinfo,
     41              {0xff, 0xe0, 0, 16, 'J', 'F', 'I', 'F', '\0',
     42               cinfo->JFIF_major_version, cinfo->JFIF_minor_version,
     43               cinfo->density_unit, static_cast<uint8_t>(cinfo->X_density >> 8),
     44               static_cast<uint8_t>(cinfo->X_density & 0xff),
     45               static_cast<uint8_t>(cinfo->Y_density >> 8),
     46               static_cast<uint8_t>(cinfo->Y_density & 0xff), 0, 0});
     47 }
     48 
     49 void EncodeAPP14(j_compress_ptr cinfo) {
     50  uint8_t color_transform = cinfo->jpeg_color_space == JCS_YCbCr  ? 1
     51                            : cinfo->jpeg_color_space == JCS_YCCK ? 2
     52                                                                  : 0;
     53  WriteOutput(cinfo, {0xff, 0xee, 0, 14, 'A', 'd', 'o', 'b', 'e', 0, 100, 0, 0,
     54                      0, 0, color_transform});
     55 }
     56 
     57 void WriteFileHeader(j_compress_ptr cinfo) {
     58  WriteOutput(cinfo, {0xFF, 0xD8});  // SOI
     59  if (cinfo->write_JFIF_header) {
     60    EncodeAPP0(cinfo);
     61  }
     62  if (cinfo->write_Adobe_marker) {
     63    EncodeAPP14(cinfo);
     64  }
     65 }
     66 
     67 bool EncodeDQT(j_compress_ptr cinfo, bool write_all_tables) {
     68  uint8_t data[4 + NUM_QUANT_TBLS * (1 + 2 * DCTSIZE2)];  // 520 bytes
     69  size_t pos = 0;
     70  data[pos++] = 0xFF;
     71  data[pos++] = 0xDB;
     72  pos += 2;  // Length will be filled in later.
     73 
     74  int send_table[NUM_QUANT_TBLS] = {};
     75  if (write_all_tables) {
     76    for (int i = 0; i < NUM_QUANT_TBLS; ++i) {
     77      if (cinfo->quant_tbl_ptrs[i]) send_table[i] = 1;
     78    }
     79  } else {
     80    for (int c = 0; c < cinfo->num_components; ++c) {
     81      send_table[cinfo->comp_info[c].quant_tbl_no] = 1;
     82    }
     83  }
     84 
     85  bool is_baseline = true;
     86  for (int i = 0; i < NUM_QUANT_TBLS; ++i) {
     87    if (!send_table[i]) continue;
     88    JQUANT_TBL* quant_table = cinfo->quant_tbl_ptrs[i];
     89    if (quant_table == nullptr) {
     90      JPEGLI_ERROR("Missing quant table %d", i);
     91    }
     92    int precision = 0;
     93    for (UINT16 q : quant_table->quantval) {
     94      if (q > 255) {
     95        precision = 1;
     96        is_baseline = false;
     97      }
     98    }
     99    if (quant_table->sent_table) {
    100      continue;
    101    }
    102    data[pos++] = (precision << 4) + i;
    103    for (size_t j = 0; j < DCTSIZE2; ++j) {
    104      int val_idx = kJPEGNaturalOrder[j];
    105      int val = quant_table->quantval[val_idx];
    106      if (val == 0) {
    107        JPEGLI_ERROR("Invalid quantval 0.");
    108      }
    109      if (precision) {
    110        data[pos++] = val >> 8;
    111      }
    112      data[pos++] = val & 0xFFu;
    113    }
    114    quant_table->sent_table = TRUE;
    115  }
    116  if (pos > 4) {
    117    data[2] = (pos - 2) >> 8u;
    118    data[3] = (pos - 2) & 0xFFu;
    119    WriteOutput(cinfo, data, pos);
    120  }
    121  return is_baseline;
    122 }
    123 
    124 void EncodeSOF(j_compress_ptr cinfo, bool is_baseline) {
    125  if (cinfo->data_precision != kJpegPrecision) {
    126    JPEGLI_ERROR("Unsupported data precision %d", cinfo->data_precision);
    127  }
    128  const uint8_t marker = cinfo->progressive_mode ? 0xc2
    129                         : is_baseline           ? 0xc0
    130                                                 : 0xc1;
    131  const size_t n_comps = cinfo->num_components;
    132  const size_t marker_len = 8 + 3 * n_comps;
    133  std::vector<uint8_t> data(marker_len + 2);
    134  size_t pos = 0;
    135  data[pos++] = 0xFF;
    136  data[pos++] = marker;
    137  data[pos++] = marker_len >> 8u;
    138  data[pos++] = marker_len & 0xFFu;
    139  data[pos++] = kJpegPrecision;
    140  data[pos++] = cinfo->image_height >> 8u;
    141  data[pos++] = cinfo->image_height & 0xFFu;
    142  data[pos++] = cinfo->image_width >> 8u;
    143  data[pos++] = cinfo->image_width & 0xFFu;
    144  data[pos++] = n_comps;
    145  for (size_t i = 0; i < n_comps; ++i) {
    146    jpeg_component_info* comp = &cinfo->comp_info[i];
    147    data[pos++] = comp->component_id;
    148    data[pos++] = ((comp->h_samp_factor << 4u) | (comp->v_samp_factor));
    149    const uint32_t quant_idx = comp->quant_tbl_no;
    150    if (cinfo->quant_tbl_ptrs[quant_idx] == nullptr) {
    151      JPEGLI_ERROR("Invalid component quant table index %u.", quant_idx);
    152    }
    153    data[pos++] = quant_idx;
    154  }
    155  WriteOutput(cinfo, data);
    156 }
    157 
    158 void WriteFrameHeader(j_compress_ptr cinfo) {
    159  jpeg_comp_master* m = cinfo->master;
    160  bool is_baseline = EncodeDQT(cinfo, /*write_all_tables=*/false);
    161  if (cinfo->progressive_mode || cinfo->arith_code ||
    162      cinfo->data_precision != 8) {
    163    is_baseline = false;
    164  }
    165  for (size_t i = 0; i < m->num_huffman_tables; ++i) {
    166    int slot_id = m->slot_id_map[i];
    167    if (slot_id > 0x11 || (slot_id > 0x01 && slot_id < 0x10)) {
    168      is_baseline = false;
    169    }
    170  }
    171  EncodeSOF(cinfo, is_baseline);
    172 }
    173 
    174 void EncodeDRI(j_compress_ptr cinfo) {
    175  WriteOutput(cinfo, {0xFF, 0xDD, 0, 4,
    176                      static_cast<uint8_t>(cinfo->restart_interval >> 8),
    177                      static_cast<uint8_t>(cinfo->restart_interval & 0xFF)});
    178 }
    179 
    180 void EncodeDHT(j_compress_ptr cinfo, size_t offset, size_t num) {
    181  jpeg_comp_master* m = cinfo->master;
    182  size_t marker_len = 2;
    183  for (size_t i = 0; i < num; ++i) {
    184    const JHUFF_TBL& table = m->huffman_tables[offset + i];
    185    if (table.sent_table) continue;
    186    marker_len += kJpegHuffmanMaxBitLength + 1;
    187    for (size_t j = 0; j <= kJpegHuffmanMaxBitLength; ++j) {
    188      marker_len += table.bits[j];
    189    }
    190  }
    191  std::vector<uint8_t> data(marker_len + 2);
    192  size_t pos = 0;
    193  data[pos++] = 0xFF;
    194  data[pos++] = 0xC4;
    195  data[pos++] = marker_len >> 8u;
    196  data[pos++] = marker_len & 0xFFu;
    197  for (size_t i = 0; i < num; ++i) {
    198    const JHUFF_TBL& table = m->huffman_tables[offset + i];
    199    if (table.sent_table) continue;
    200    size_t total_count = 0;
    201    for (size_t i = 0; i <= kJpegHuffmanMaxBitLength; ++i) {
    202      total_count += table.bits[i];
    203    }
    204    data[pos++] = m->slot_id_map[offset + i];
    205    for (size_t i = 1; i <= kJpegHuffmanMaxBitLength; ++i) {
    206      data[pos++] = table.bits[i];
    207    }
    208    for (size_t i = 0; i < total_count; ++i) {
    209      data[pos++] = table.huffval[i];
    210    }
    211  }
    212  if (marker_len > 2) {
    213    WriteOutput(cinfo, data);
    214  }
    215 }
    216 
    217 void EncodeSOS(j_compress_ptr cinfo, int scan_index) {
    218  jpeg_comp_master* m = cinfo->master;
    219  const jpeg_scan_info* scan_info = &cinfo->scan_info[scan_index];
    220  const size_t marker_len = 6 + 2 * scan_info->comps_in_scan;
    221  std::vector<uint8_t> data(marker_len + 2);
    222  size_t pos = 0;
    223  data[pos++] = 0xFF;
    224  data[pos++] = 0xDA;
    225  data[pos++] = marker_len >> 8u;
    226  data[pos++] = marker_len & 0xFFu;
    227  data[pos++] = scan_info->comps_in_scan;
    228  for (int i = 0; i < scan_info->comps_in_scan; ++i) {
    229    int comp_idx = scan_info->component_index[i];
    230    data[pos++] = cinfo->comp_info[comp_idx].component_id;
    231    int dc_slot_id = m->slot_id_map[m->context_map[comp_idx]];
    232    int ac_context = m->ac_ctx_offset[scan_index] + i;
    233    int ac_slot_id = m->slot_id_map[m->context_map[ac_context]];
    234    data[pos++] = (dc_slot_id << 4u) + (ac_slot_id - 16);
    235  }
    236  data[pos++] = scan_info->Ss;
    237  data[pos++] = scan_info->Se;
    238  data[pos++] = ((scan_info->Ah << 4u) | (scan_info->Al));
    239  WriteOutput(cinfo, data);
    240 }
    241 
    242 void WriteScanHeader(j_compress_ptr cinfo, int scan_index) {
    243  jpeg_comp_master* m = cinfo->master;
    244  const jpeg_scan_info* scan_info = &cinfo->scan_info[scan_index];
    245  cinfo->restart_interval = m->scan_token_info[scan_index].restart_interval;
    246  if (cinfo->restart_interval != m->last_restart_interval) {
    247    EncodeDRI(cinfo);
    248    m->last_restart_interval = cinfo->restart_interval;
    249  }
    250  size_t num_dht = 0;
    251  if (scan_index == 0) {
    252    // For the first scan we emit all DC and at most 4 AC Huffman codes.
    253    for (size_t i = 0, num_ac = 0; i < m->num_huffman_tables; ++i) {
    254      if (m->slot_id_map[i] >= 16 && num_ac++ >= 4) break;
    255      ++num_dht;
    256    }
    257  } else if (scan_info->Ss > 0) {
    258    // For multi-scan sequential and progressive DC scans we have already
    259    // emitted all Huffman codes that we need before the first scan. For
    260    // progressive AC scans we only need at most one new Huffman code.
    261    if (m->context_map[m->ac_ctx_offset[scan_index]] == m->next_dht_index) {
    262      num_dht = 1;
    263    }
    264  }
    265  if (num_dht > 0) {
    266    EncodeDHT(cinfo, m->next_dht_index, num_dht);
    267    m->next_dht_index += num_dht;
    268  }
    269  EncodeSOS(cinfo, scan_index);
    270 }
    271 
    272 void WriteBlock(const int32_t* JXL_RESTRICT symbols,
    273                const int32_t* JXL_RESTRICT extra_bits, const int num_nonzeros,
    274                const bool emit_eob,
    275                const HuffmanCodeTable* JXL_RESTRICT dc_code,
    276                const HuffmanCodeTable* JXL_RESTRICT ac_code,
    277                JpegBitWriter* JXL_RESTRICT bw) {
    278  int symbol = symbols[0];
    279  WriteBits(bw, dc_code->depth[symbol], dc_code->code[symbol] | extra_bits[0]);
    280  for (int i = 1; i < num_nonzeros; ++i) {
    281    symbol = symbols[i];
    282    if (symbol > 255) {
    283      WriteBits(bw, ac_code->depth[0xf0], ac_code->code[0xf0]);
    284      symbol -= 256;
    285      if (symbol > 255) {
    286        WriteBits(bw, ac_code->depth[0xf0], ac_code->code[0xf0]);
    287        symbol -= 256;
    288        if (symbol > 255) {
    289          WriteBits(bw, ac_code->depth[0xf0], ac_code->code[0xf0]);
    290          symbol -= 256;
    291        }
    292      }
    293    }
    294    WriteBits(bw, ac_code->depth[symbol],
    295              ac_code->code[symbol] | extra_bits[i]);
    296  }
    297  if (emit_eob) {
    298    WriteBits(bw, ac_code->depth[0], ac_code->code[0]);
    299  }
    300 }
    301 
    302 namespace {
    303 
    304 JXL_INLINE void EmitMarker(JpegBitWriter* bw, int marker) {
    305  bw->data[bw->pos++] = 0xFF;
    306  bw->data[bw->pos++] = marker;
    307 }
    308 
    309 void WriteTokens(j_compress_ptr cinfo, int scan_index, JpegBitWriter* bw) {
    310  jpeg_comp_master* m = cinfo->master;
    311  HuffmanCodeTable* coding_tables = &m->coding_tables[0];
    312  int next_restart_marker = 0;
    313  const ScanTokenInfo& sti = m->scan_token_info[scan_index];
    314  size_t num_token_arrays = m->cur_token_array + 1;
    315  size_t total_tokens = 0;
    316  size_t restart_idx = 0;
    317  size_t next_restart = sti.restarts[restart_idx];
    318  uint8_t* context_map = m->context_map;
    319  for (size_t i = 0; i < num_token_arrays; ++i) {
    320    Token* tokens = m->token_arrays[i].tokens;
    321    size_t num_tokens = m->token_arrays[i].num_tokens;
    322    if (sti.token_offset < total_tokens + num_tokens &&
    323        total_tokens < sti.token_offset + sti.num_tokens) {
    324      size_t start_ix =
    325          total_tokens < sti.token_offset ? sti.token_offset - total_tokens : 0;
    326      size_t end_ix = std::min(sti.token_offset + sti.num_tokens - total_tokens,
    327                               num_tokens);
    328      size_t cycle_len = bw->len / 8;
    329      size_t next_cycle = cycle_len;
    330      for (size_t i = start_ix; i < end_ix; ++i) {
    331        if (total_tokens + i == next_restart) {
    332          JumpToByteBoundary(bw);
    333          EmitMarker(bw, 0xD0 + next_restart_marker);
    334          next_restart_marker += 1;
    335          next_restart_marker &= 0x7;
    336          next_restart = sti.restarts[++restart_idx];
    337        }
    338        Token t = tokens[i];
    339        const HuffmanCodeTable* code = &coding_tables[context_map[t.context]];
    340        WriteBits(bw, code->depth[t.symbol], code->code[t.symbol] | t.bits);
    341        if (--next_cycle == 0) {
    342          if (!EmptyBitWriterBuffer(bw)) {
    343            JPEGLI_ERROR(
    344                "Output suspension is not supported in "
    345                "finish_compress");
    346          }
    347          next_cycle = cycle_len;
    348        }
    349      }
    350    }
    351    total_tokens += num_tokens;
    352  }
    353 }
    354 
    355 void WriteACRefinementTokens(j_compress_ptr cinfo, int scan_index,
    356                             JpegBitWriter* bw) {
    357  jpeg_comp_master* m = cinfo->master;
    358  const ScanTokenInfo& sti = m->scan_token_info[scan_index];
    359  const uint8_t context = m->ac_ctx_offset[scan_index];
    360  const HuffmanCodeTable* code = &m->coding_tables[m->context_map[context]];
    361  size_t cycle_len = bw->len / 64;
    362  size_t next_cycle = cycle_len;
    363  size_t refbit_idx = 0;
    364  size_t eobrun_idx = 0;
    365  size_t restart_idx = 0;
    366  size_t next_restart = sti.restarts[restart_idx];
    367  int next_restart_marker = 0;
    368  for (size_t i = 0; i < sti.num_tokens; ++i) {
    369    if (i == next_restart) {
    370      JumpToByteBoundary(bw);
    371      EmitMarker(bw, 0xD0 + next_restart_marker);
    372      next_restart_marker += 1;
    373      next_restart_marker &= 0x7;
    374      next_restart = sti.restarts[++restart_idx];
    375    }
    376    RefToken t = sti.tokens[i];
    377    int symbol = t.symbol & 253;
    378    uint16_t bits = 0;
    379    if ((symbol & 1) == 0) {
    380      int r = symbol >> 4;
    381      if (r > 0 && r < 15) {
    382        bits = sti.eobruns[eobrun_idx++];
    383      }
    384    } else {
    385      bits = (t.symbol >> 1) & 1;
    386    }
    387    WriteBits(bw, code->depth[symbol], code->code[symbol] | bits);
    388    for (int j = 0; j < t.refbits; ++j) {
    389      WriteBits(bw, 1, sti.refbits[refbit_idx++]);
    390    }
    391    if (--next_cycle == 0) {
    392      if (!EmptyBitWriterBuffer(bw)) {
    393        JPEGLI_ERROR("Output suspension is not supported in finish_compress");
    394      }
    395      next_cycle = cycle_len;
    396    }
    397  }
    398 }
    399 
    400 void WriteDCRefinementBits(j_compress_ptr cinfo, int scan_index,
    401                           JpegBitWriter* bw) {
    402  jpeg_comp_master* m = cinfo->master;
    403  const ScanTokenInfo& sti = m->scan_token_info[scan_index];
    404  size_t restart_idx = 0;
    405  size_t next_restart = sti.restarts[restart_idx];
    406  int next_restart_marker = 0;
    407  size_t cycle_len = bw->len * 4;
    408  size_t next_cycle = cycle_len;
    409  size_t refbit_idx = 0;
    410  for (size_t i = 0; i < sti.num_tokens; ++i) {
    411    if (i == next_restart) {
    412      JumpToByteBoundary(bw);
    413      EmitMarker(bw, 0xD0 + next_restart_marker);
    414      next_restart_marker += 1;
    415      next_restart_marker &= 0x7;
    416      next_restart = sti.restarts[++restart_idx];
    417    }
    418    WriteBits(bw, 1, sti.refbits[refbit_idx++]);
    419    if (--next_cycle == 0) {
    420      if (!EmptyBitWriterBuffer(bw)) {
    421        JPEGLI_ERROR(
    422            "Output suspension is not supported in "
    423            "finish_compress");
    424      }
    425      next_cycle = cycle_len;
    426    }
    427  }
    428 }
    429 
    430 }  // namespace
    431 
    432 void WriteScanData(j_compress_ptr cinfo, int scan_index) {
    433  const jpeg_scan_info* scan_info = &cinfo->scan_info[scan_index];
    434  JpegBitWriter* bw = &cinfo->master->bw;
    435  if (scan_info->Ah == 0) {
    436    WriteTokens(cinfo, scan_index, bw);
    437  } else if (scan_info->Ss > 0) {
    438    WriteACRefinementTokens(cinfo, scan_index, bw);
    439  } else {
    440    WriteDCRefinementBits(cinfo, scan_index, bw);
    441  }
    442  if (!bw->healthy) {
    443    JPEGLI_ERROR("Unknown Huffman coded symbol found in scan %d", scan_index);
    444  }
    445  JumpToByteBoundary(bw);
    446  if (!EmptyBitWriterBuffer(bw)) {
    447    JPEGLI_ERROR("Output suspension is not supported in finish_compress");
    448  }
    449 }
    450 
    451 }  // namespace jpegli