libjxl

FORK: libjxl patches used on blog
git clone https://git.neptards.moe/blog/libjxl.git
Log | Files | Refs | Submodules | 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