/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 | 26.5M | size_t prev) { |
67 | 26.5M | JXL_DASSERT((static_cast<size_t>(1) << log2_covered_blocks) == |
68 | 26.5M | covered_blocks); |
69 | 26.5M | nonzeros_left = (nonzeros_left + covered_blocks - 1) >> log2_covered_blocks; |
70 | 26.5M | k >>= log2_covered_blocks; |
71 | 26.5M | JXL_DASSERT(k > 0); |
72 | 26.5M | JXL_DASSERT(k < 64); |
73 | 26.5M | 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 | 26.5M | JXL_DASSERT(nonzeros_left < 64); |
81 | 26.5M | return (kCoeffNumNonzeroContext[nonzeros_left] + kCoeffFreqContext[k]) * 2 + |
82 | 26.5M | prev; |
83 | 26.5M | } Unexecuted instantiation: encode.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_context_map.cc:jxl::ZeroDensityContext(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long) Unexecuted instantiation: enc_lz77.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_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_dot_dictionary.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) Unexecuted instantiation: enc_debug_image.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: enc_heuristics.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_cache.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_ac_strategy.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 | 26.0M | size_t prev) { | 67 | 26.0M | JXL_DASSERT((static_cast<size_t>(1) << log2_covered_blocks) == | 68 | 26.0M | covered_blocks); | 69 | 26.0M | nonzeros_left = (nonzeros_left + covered_blocks - 1) >> log2_covered_blocks; | 70 | 26.0M | k >>= log2_covered_blocks; | 71 | 26.0M | JXL_DASSERT(k > 0); | 72 | 26.0M | JXL_DASSERT(k < 64); | 73 | 26.0M | 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 | 26.0M | JXL_DASSERT(nonzeros_left < 64); | 81 | 26.0M | return (kCoeffNumNonzeroContext[nonzeros_left] + kCoeffFreqContext[k]) * 2 + | 82 | 26.0M | prev; | 83 | 26.0M | } |
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 | 496k | size_t prev) { | 67 | 496k | JXL_DASSERT((static_cast<size_t>(1) << log2_covered_blocks) == | 68 | 496k | covered_blocks); | 69 | 496k | nonzeros_left = (nonzeros_left + covered_blocks - 1) >> log2_covered_blocks; | 70 | 496k | k >>= log2_covered_blocks; | 71 | 496k | JXL_DASSERT(k > 0); | 72 | 496k | JXL_DASSERT(k < 64); | 73 | 496k | 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 | 496k | JXL_DASSERT(nonzeros_left < 64); | 81 | 496k | return (kCoeffNumNonzeroContext[nonzeros_left] + kCoeffFreqContext[k]) * 2 + | 82 | 496k | prev; | 83 | 496k | } |
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: decode.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: quant_weights.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) |
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 | 998k | size_t Context(int dc_idx, uint32_t qf, size_t ord, size_t c) const { |
102 | 998k | size_t qf_idx = 0; |
103 | 998k | for (uint32_t t : qf_thresholds) { |
104 | 405k | if (qf > t) qf_idx++; |
105 | 405k | } |
106 | 998k | size_t idx = c < 2 ? c ^ 1 : 2; |
107 | 998k | idx = idx * kNumOrders + ord; |
108 | 998k | idx = idx * (qf_thresholds.size() + 1) + qf_idx; |
109 | 998k | idx = idx * num_dc_ctxs + dc_idx; |
110 | 998k | return ctx_map[idx]; |
111 | 998k | } |
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 | 997k | constexpr uint32_t ZeroDensityContextsOffset(uint32_t block_ctx) const { |
115 | 997k | return static_cast<uint32_t>(num_ctxs * kNonZeroBuckets + |
116 | 997k | kZeroDensityContextCount * block_ctx); |
117 | 997k | } |
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 | 4.44k | constexpr uint32_t NumACContexts() const { |
128 | 4.44k | return static_cast<uint32_t>(num_ctxs * |
129 | 4.44k | (kNonZeroBuckets + kZeroDensityContextCount)); |
130 | 4.44k | } |
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 | 998k | inline uint32_t NonZeroContext(uint32_t non_zeros, uint32_t block_ctx) const { |
135 | 998k | uint32_t ctx; |
136 | 998k | if (non_zeros >= 64) non_zeros = 64; |
137 | 998k | if (non_zeros < 8) { |
138 | 632k | ctx = non_zeros; |
139 | 632k | } else { |
140 | 365k | ctx = 4 + non_zeros / 2; |
141 | 365k | } |
142 | 998k | return static_cast<uint32_t>(ctx * num_ctxs + block_ctx); |
143 | 998k | } |
144 | | |
145 | 13.5k | BlockCtxMap() { |
146 | 13.5k | ctx_map.assign(std::begin(kDefaultCtxMap), std::end(kDefaultCtxMap)); |
147 | 13.5k | num_ctxs = *std::max_element(ctx_map.begin(), ctx_map.end()) + 1; |
148 | 13.5k | num_dc_ctxs = 1; |
149 | 13.5k | } |
150 | | }; |
151 | | |
152 | | } // namespace jxl |
153 | | |
154 | | #endif // LIB_JXL_AC_CONTEXT_H_ |