libjxl

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

ans_common.cc (6256B)


      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/jxl/ans_common.h"
      7 
      8 #include <numeric>
      9 
     10 #include "lib/jxl/ans_params.h"
     11 #include "lib/jxl/base/status.h"
     12 
     13 namespace jxl {
     14 
     15 std::vector<int32_t> CreateFlatHistogram(int length, int total_count) {
     16   JXL_ASSERT(length > 0);
     17   JXL_ASSERT(length <= total_count);
     18   const int count = total_count / length;
     19   std::vector<int32_t> result(length, count);
     20   const int rem_counts = total_count % length;
     21   for (int i = 0; i < rem_counts; ++i) {
     22     ++result[i];
     23   }
     24   return result;
     25 }
     26 
     27 // First, all trailing non-occurring symbols are removed from the distribution;
     28 // if this leaves the distribution empty, a placeholder symbol with max weight
     29 // is  added. This ensures that the resulting distribution sums to total table
     30 // size. Then, `entry_size` is chosen to be the largest power of two so that
     31 // `table_size` = ANS_TAB_SIZE/`entry_size` is at least as big as the
     32 // distribution size.
     33 // Note that each entry will only ever contain two different symbols, and
     34 // consecutive ranges of offsets, which allows us to use a compact
     35 // representation.
     36 // Each entry is initialized with only the (symbol=i, offset) pairs; then
     37 // positions for which the entry overflows (i.e. distribution[i] > entry_size)
     38 // or is not full are computed, and put into a stack in increasing order.
     39 // Missing symbols in the distribution are padded with 0 (because `table_size`
     40 // >= number of symbols). The `cutoff` value for each entry is initialized to
     41 // the number of occupied slots in that entry (i.e. `distributions[i]`). While
     42 // the overflowing-symbol stack is not empty (which implies that the
     43 // underflowing-symbol stack also is not), the top overfull and underfull
     44 // positions are popped from the stack; the empty slots in the underfull entry
     45 // are then filled with as many slots as needed from the overfull entry; such
     46 // slots are placed after the slots in the overfull entry, and `offsets[1]` is
     47 // computed accordingly. The formerly underfull entry is thus now neither
     48 // underfull nor overfull, and represents exactly two symbols. The overfull
     49 // entry might be either overfull or underfull, and is pushed into the
     50 // corresponding stack.
     51 void InitAliasTable(std::vector<int32_t> distribution, uint32_t range,
     52                     size_t log_alpha_size, AliasTable::Entry* JXL_RESTRICT a) {
     53   while (!distribution.empty() && distribution.back() == 0) {
     54     distribution.pop_back();
     55   }
     56   // Ensure that a valid table is always returned, even for an empty
     57   // alphabet. Otherwise, a specially-crafted stream might crash the
     58   // decoder.
     59   if (distribution.empty()) {
     60     distribution.emplace_back(range);
     61   }
     62   const size_t table_size = 1 << log_alpha_size;
     63 #if JXL_ENABLE_ASSERT
     64   int sum = std::accumulate(distribution.begin(), distribution.end(), 0);
     65 #endif  // JXL_ENABLE_ASSERT
     66   JXL_ASSERT(static_cast<uint32_t>(sum) == range);
     67   // range must be a power of two
     68   JXL_ASSERT((range & (range - 1)) == 0);
     69   JXL_ASSERT(distribution.size() <= table_size);
     70   JXL_ASSERT(table_size <= range);
     71   const uint32_t entry_size = range >> log_alpha_size;  // this is exact
     72   // Special case for single-symbol distributions, that ensures that the state
     73   // does not change when decoding from such a distribution. Note that, since we
     74   // hardcode offset0 == 0, it is not straightforward (if at all possible) to
     75   // fix the general case to produce this result.
     76   for (size_t sym = 0; sym < distribution.size(); sym++) {
     77     if (distribution[sym] == ANS_TAB_SIZE) {
     78       for (size_t i = 0; i < table_size; i++) {
     79         a[i].right_value = sym;
     80         a[i].cutoff = 0;
     81         a[i].offsets1 = entry_size * i;
     82         a[i].freq0 = 0;
     83         a[i].freq1_xor_freq0 = ANS_TAB_SIZE;
     84       }
     85       return;
     86     }
     87   }
     88   std::vector<uint32_t> underfull_posn;
     89   std::vector<uint32_t> overfull_posn;
     90   std::vector<uint32_t> cutoffs(1 << log_alpha_size);
     91   // Initialize entries.
     92   for (size_t i = 0; i < distribution.size(); i++) {
     93     cutoffs[i] = distribution[i];
     94     if (cutoffs[i] > entry_size) {
     95       overfull_posn.push_back(i);
     96     } else if (cutoffs[i] < entry_size) {
     97       underfull_posn.push_back(i);
     98     }
     99   }
    100   for (uint32_t i = distribution.size(); i < table_size; i++) {
    101     cutoffs[i] = 0;
    102     underfull_posn.push_back(i);
    103   }
    104   // Reassign overflow/underflow values.
    105   while (!overfull_posn.empty()) {
    106     uint32_t overfull_i = overfull_posn.back();
    107     overfull_posn.pop_back();
    108     JXL_ASSERT(!underfull_posn.empty());
    109     uint32_t underfull_i = underfull_posn.back();
    110     underfull_posn.pop_back();
    111     uint32_t underfull_by = entry_size - cutoffs[underfull_i];
    112     cutoffs[overfull_i] -= underfull_by;
    113     // overfull positions have their original symbols
    114     a[underfull_i].right_value = overfull_i;
    115     a[underfull_i].offsets1 = cutoffs[overfull_i];
    116     // Slots in the right part of entry underfull_i were taken from the end
    117     // of the symbols in entry overfull_i.
    118     if (cutoffs[overfull_i] < entry_size) {
    119       underfull_posn.push_back(overfull_i);
    120     } else if (cutoffs[overfull_i] > entry_size) {
    121       overfull_posn.push_back(overfull_i);
    122     }
    123   }
    124   for (uint32_t i = 0; i < table_size; i++) {
    125     // cutoffs[i] is properly initialized but the clang-analyzer doesn't infer
    126     // it since it is partially initialized across two for-loops.
    127     // NOLINTNEXTLINE(clang-analyzer-core.UndefinedBinaryOperatorResult)
    128     if (cutoffs[i] == entry_size) {
    129       a[i].right_value = i;
    130       a[i].offsets1 = 0;
    131       a[i].cutoff = 0;
    132     } else {
    133       // Note that, if cutoff is not equal to entry_size,
    134       // a[i].offsets1 was initialized with (overfull cutoff) -
    135       // (entry_size - a[i].cutoff). Thus, subtracting
    136       // a[i].cutoff cannot make it negative.
    137       a[i].offsets1 -= cutoffs[i];
    138       a[i].cutoff = cutoffs[i];
    139     }
    140     const size_t freq0 = i < distribution.size() ? distribution[i] : 0;
    141     const size_t i1 = a[i].right_value;
    142     const size_t freq1 = i1 < distribution.size() ? distribution[i1] : 0;
    143     a[i].freq0 = static_cast<uint16_t>(freq0);
    144     a[i].freq1_xor_freq0 = static_cast<uint16_t>(freq1 ^ freq0);
    145   }
    146 }
    147 
    148 }  // namespace jxl