Coverage Report

Created: 2025-07-23 08:18

/src/libjxl/lib/jxl/ac_context.h
Line
Count
Source (jump to first uncovered line)
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
#ifndef LIB_JXL_AC_CONTEXT_H_
7
#define LIB_JXL_AC_CONTEXT_H_
8
9
#include <algorithm>
10
#include <cstddef>
11
#include <cstdint>
12
#include <vector>
13
14
#include "lib/jxl/base/compiler_specific.h"
15
#include "lib/jxl/base/status.h"
16
#include "lib/jxl/coeff_order_fwd.h"
17
18
namespace jxl {
19
20
// Block context used for scanning order, number of non-zeros, AC coefficients.
21
// Equal to the channel.
22
constexpr uint32_t kDCTOrderContextStart = 0;
23
24
// The number of predicted nonzeros goes from 0 to 1008. We use
25
// ceil(log2(predicted+1)) as a context for the number of nonzeros, so from 0 to
26
// 10, inclusive.
27
constexpr uint32_t kNonZeroBuckets = 37;
28
29
static const uint16_t kCoeffFreqContext[64] = {
30
    0xBAD, 0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 11, 12, 13, 14,
31
    15,    15, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22,
32
    23,    23, 23, 23, 24, 24, 24, 24, 25, 25, 25, 25, 26, 26, 26, 26,
33
    27,    27, 27, 27, 28, 28, 28, 28, 29, 29, 29, 29, 30, 30, 30, 30,
34
};
35
36
static const uint16_t kCoeffNumNonzeroContext[64] = {
37
    0xBAD, 0,   31,  62,  62,  93,  93,  93,  93,  123, 123, 123, 123,
38
    152,   152, 152, 152, 152, 152, 152, 152, 180, 180, 180, 180, 180,
39
    180,   180, 180, 180, 180, 180, 180, 206, 206, 206, 206, 206, 206,
40
    206,   206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206,
41
    206,   206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206,
42
};
43
44
// Supremum of ZeroDensityContext(x, y) + 1, when x + y < 64.
45
constexpr int kZeroDensityContextCount = 458;
46
// Supremum of ZeroDensityContext(x, y) + 1.
47
constexpr int kZeroDensityContextLimit = 474;
48
49
/* This function is used for entropy-sources pre-clustering.
50
 *
51
 * Ideally, each combination of |nonzeros_left| and |k| should go to its own
52
 * bucket; but it implies (64 * 63 / 2) == 2016 buckets. If there is other
53
 * dimension (e.g. block context), then number of primary clusters becomes too
54
 * big.
55
 *
56
 * To solve this problem, |nonzeros_left| and |k| values are clustered. It is
57
 * known that their sum is at most 64, consequently, the total number buckets
58
 * is at most A(64) * B(64).
59
 */
60
// TODO(user): investigate, why disabling pre-clustering makes entropy code
61
// less dense. Perhaps we would need to add HQ clustering algorithm that would
62
// be able to squeeze better by spending more CPU cycles.
63
static JXL_INLINE size_t ZeroDensityContext(size_t nonzeros_left, size_t k,
64
                                            size_t covered_blocks,
65
                                            size_t log2_covered_blocks,
66
146M
                                            size_t prev) {
67
146M
  JXL_DASSERT((static_cast<size_t>(1) << log2_covered_blocks) ==
68
146M
              covered_blocks);
69
146M
  nonzeros_left = (nonzeros_left + covered_blocks - 1) >> log2_covered_blocks;
70
146M
  k >>= log2_covered_blocks;
71
146M
  JXL_DASSERT(k > 0);
72
146M
  JXL_DASSERT(k < 64);
73
146M
  JXL_DASSERT(nonzeros_left > 0);
74
  // Asserting nonzeros_left + k < 65 here causes crashes in debug mode with
75
  // invalid input, since the (hot) decoding loop does not check this condition.
76
  // As no out-of-bound memory reads are issued even if that condition is
77
  // broken, we check this simpler condition which holds anyway. The decoder
78
  // will still mark a file in which that condition happens as not valid at the
79
  // end of the decoding loop, as `nzeros` will not be `0`.
80
146M
  JXL_DASSERT(nonzeros_left < 64);
81
146M
  return (kCoeffNumNonzeroContext[nonzeros_left] + kCoeffFreqContext[k]) * 2 +
82
146M
         prev;
83
146M
}
Unexecuted instantiation: encode.cc:jxl::ZeroDensityContext(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long)
Unexecuted instantiation: decode.cc:jxl::ZeroDensityContext(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long)
Unexecuted instantiation: quant_weights.cc:jxl::ZeroDensityContext(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long)
Unexecuted instantiation: enc_frame.cc:jxl::ZeroDensityContext(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long)
Unexecuted instantiation: enc_group.cc:jxl::ZeroDensityContext(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long)
Unexecuted instantiation: enc_heuristics.cc:jxl::ZeroDensityContext(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long)
Unexecuted instantiation: enc_modular.cc:jxl::ZeroDensityContext(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long)
Unexecuted instantiation: enc_patch_dictionary.cc:jxl::ZeroDensityContext(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long)
Unexecuted instantiation: enc_quant_weights.cc:jxl::ZeroDensityContext(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long)
Unexecuted instantiation: compressed_dc.cc:jxl::ZeroDensityContext(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long)
Unexecuted instantiation: dec_cache.cc:jxl::ZeroDensityContext(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long)
Unexecuted instantiation: dec_external_image.cc:jxl::ZeroDensityContext(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long)
Unexecuted instantiation: dec_frame.cc:jxl::ZeroDensityContext(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long)
dec_group.cc:jxl::ZeroDensityContext(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long)
Line
Count
Source
66
541k
                                            size_t prev) {
67
541k
  JXL_DASSERT((static_cast<size_t>(1) << log2_covered_blocks) ==
68
541k
              covered_blocks);
69
541k
  nonzeros_left = (nonzeros_left + covered_blocks - 1) >> log2_covered_blocks;
70
541k
  k >>= log2_covered_blocks;
71
541k
  JXL_DASSERT(k > 0);
72
541k
  JXL_DASSERT(k < 64);
73
541k
  JXL_DASSERT(nonzeros_left > 0);
74
  // Asserting nonzeros_left + k < 65 here causes crashes in debug mode with
75
  // invalid input, since the (hot) decoding loop does not check this condition.
76
  // As no out-of-bound memory reads are issued even if that condition is
77
  // broken, we check this simpler condition which holds anyway. The decoder
78
  // will still mark a file in which that condition happens as not valid at the
79
  // end of the decoding loop, as `nzeros` will not be `0`.
80
541k
  JXL_DASSERT(nonzeros_left < 64);
81
541k
  return (kCoeffNumNonzeroContext[nonzeros_left] + kCoeffFreqContext[k]) * 2 +
82
541k
         prev;
83
541k
}
Unexecuted instantiation: dec_modular.cc:jxl::ZeroDensityContext(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long)
Unexecuted instantiation: dec_noise.cc:jxl::ZeroDensityContext(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long)
Unexecuted instantiation: entropy_coder.cc:jxl::ZeroDensityContext(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long)
Unexecuted instantiation: epf.cc:jxl::ZeroDensityContext(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long)
Unexecuted instantiation: passes_state.cc:jxl::ZeroDensityContext(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long)
Unexecuted instantiation: stage_blending.cc:jxl::ZeroDensityContext(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long)
Unexecuted instantiation: stage_epf.cc:jxl::ZeroDensityContext(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long)
Unexecuted instantiation: stage_write.cc:jxl::ZeroDensityContext(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long)
Unexecuted instantiation: enc_ac_strategy.cc:jxl::ZeroDensityContext(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long)
Unexecuted instantiation: enc_adaptive_quantization.cc:jxl::ZeroDensityContext(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long)
Unexecuted instantiation: enc_ans.cc:jxl::ZeroDensityContext(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long)
Unexecuted instantiation: enc_cache.cc:jxl::ZeroDensityContext(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long)
Unexecuted instantiation: enc_context_map.cc:jxl::ZeroDensityContext(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long)
Unexecuted instantiation: enc_debug_image.cc:jxl::ZeroDensityContext(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long)
Unexecuted instantiation: enc_dot_dictionary.cc:jxl::ZeroDensityContext(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long)
enc_entropy_coder.cc:jxl::ZeroDensityContext(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long)
Line
Count
Source
66
145M
                                            size_t prev) {
67
145M
  JXL_DASSERT((static_cast<size_t>(1) << log2_covered_blocks) ==
68
145M
              covered_blocks);
69
145M
  nonzeros_left = (nonzeros_left + covered_blocks - 1) >> log2_covered_blocks;
70
145M
  k >>= log2_covered_blocks;
71
145M
  JXL_DASSERT(k > 0);
72
145M
  JXL_DASSERT(k < 64);
73
145M
  JXL_DASSERT(nonzeros_left > 0);
74
  // Asserting nonzeros_left + k < 65 here causes crashes in debug mode with
75
  // invalid input, since the (hot) decoding loop does not check this condition.
76
  // As no out-of-bound memory reads are issued even if that condition is
77
  // broken, we check this simpler condition which holds anyway. The decoder
78
  // will still mark a file in which that condition happens as not valid at the
79
  // end of the decoding loop, as `nzeros` will not be `0`.
80
145M
  JXL_DASSERT(nonzeros_left < 64);
81
145M
  return (kCoeffNumNonzeroContext[nonzeros_left] + kCoeffFreqContext[k]) * 2 +
82
145M
         prev;
83
145M
}
Unexecuted instantiation: enc_lz77.cc:jxl::ZeroDensityContext(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long)
Unexecuted instantiation: enc_detect_dots.cc:jxl::ZeroDensityContext(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long)
84
85
struct BlockCtxMap {
86
  std::vector<int> dc_thresholds[3];
87
  std::vector<uint32_t> qf_thresholds;
88
  std::vector<uint8_t> ctx_map;
89
  size_t num_ctxs, num_dc_ctxs;
90
91
  static constexpr uint8_t kDefaultCtxMap[] = {
92
      // Default ctx map clusters all the large transforms together.
93
      0, 1, 2, 2, 3,  3,  4,  5,  6,  6,  6,  6,  6,   //
94
      7, 8, 9, 9, 10, 11, 12, 13, 14, 14, 14, 14, 14,  //
95
      7, 8, 9, 9, 10, 11, 12, 13, 14, 14, 14, 14, 14,  //
96
  };
97
  static_assert(3 * kNumOrders ==
98
                    sizeof(kDefaultCtxMap) / sizeof *kDefaultCtxMap,
99
                "Update default context map");
100
101
5.16M
  size_t Context(int dc_idx, uint32_t qf, size_t ord, size_t c) const {
102
5.16M
    size_t qf_idx = 0;
103
5.16M
    for (uint32_t t : qf_thresholds) {
104
156k
      if (qf > t) qf_idx++;
105
156k
    }
106
5.16M
    size_t idx = c < 2 ? c ^ 1 : 2;
107
5.16M
    idx = idx * kNumOrders + ord;
108
5.16M
    idx = idx * (qf_thresholds.size() + 1) + qf_idx;
109
5.16M
    idx = idx * num_dc_ctxs + dc_idx;
110
5.16M
    return ctx_map[idx];
111
5.16M
  }
112
  // Non-zero context is based on number of non-zeros and block context.
113
  // For better clustering, contexts with same number of non-zeros are grouped.
114
5.16M
  constexpr uint32_t ZeroDensityContextsOffset(uint32_t block_ctx) const {
115
5.16M
    return static_cast<uint32_t>(num_ctxs * kNonZeroBuckets +
116
5.16M
                                 kZeroDensityContextCount * block_ctx);
117
5.16M
  }
118
119
  // Context map for AC coefficients consists of 2 blocks:
120
  //  |num_ctxs x                : context for number of non-zeros in the block
121
  //   kNonZeroBuckets|            computed from block context and predicted
122
  //                               value (based top and left values)
123
  //  |num_ctxs x                : context for AC coefficient symbols,
124
  //   kZeroDensityContextCount|   computed from block context,
125
  //                               number of non-zeros left and
126
  //                               index in scan order
127
11.8k
  constexpr uint32_t NumACContexts() const {
128
11.8k
    return static_cast<uint32_t>(num_ctxs *
129
11.8k
                                 (kNonZeroBuckets + kZeroDensityContextCount));
130
11.8k
  }
131
132
  // Non-zero context is based on number of non-zeros and block context.
133
  // For better clustering, contexts with same number of non-zeros are grouped.
134
5.16M
  inline uint32_t NonZeroContext(uint32_t non_zeros, uint32_t block_ctx) const {
135
5.16M
    uint32_t ctx;
136
5.16M
    if (non_zeros >= 64) non_zeros = 64;
137
5.16M
    if (non_zeros < 8) {
138
3.07M
      ctx = non_zeros;
139
3.07M
    } else {
140
2.09M
      ctx = 4 + non_zeros / 2;
141
2.09M
    }
142
5.16M
    return static_cast<uint32_t>(ctx * num_ctxs + block_ctx);
143
5.16M
  }
144
145
65.8k
  BlockCtxMap() {
146
65.8k
    ctx_map.assign(std::begin(kDefaultCtxMap), std::end(kDefaultCtxMap));
147
65.8k
    num_ctxs = *std::max_element(ctx_map.begin(), ctx_map.end()) + 1;
148
65.8k
    num_dc_ctxs = 1;
149
65.8k
  }
150
};
151
152
}  // namespace jxl
153
154
#endif  // LIB_JXL_AC_CONTEXT_H_