/src/libjxl/lib/jxl/enc_context_map.cc
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 | | // Library to encode the context map. |
7 | | |
8 | | #include "lib/jxl/enc_context_map.h" |
9 | | |
10 | | #include <jxl/memory_manager.h> |
11 | | #include <jxl/types.h> |
12 | | |
13 | | #include <algorithm> |
14 | | #include <cstddef> |
15 | | #include <cstdint> |
16 | | #include <vector> |
17 | | |
18 | | #include "lib/jxl/ac_context.h" |
19 | | #include "lib/jxl/base/bits.h" |
20 | | #include "lib/jxl/base/status.h" |
21 | | #include "lib/jxl/enc_ans.h" |
22 | | #include "lib/jxl/enc_ans_params.h" |
23 | | #include "lib/jxl/enc_aux_out.h" |
24 | | #include "lib/jxl/entropy_coder.h" |
25 | | #include "lib/jxl/fields.h" |
26 | | #include "lib/jxl/pack_signed.h" |
27 | | |
28 | | namespace jxl { |
29 | | |
30 | | namespace { |
31 | | |
32 | 274k | size_t IndexOf(const std::vector<uint8_t>& v, uint8_t value) { |
33 | 274k | size_t i = 0; |
34 | 3.39M | for (; i < v.size(); ++i) { |
35 | 3.39M | if (v[i] == value) return i; |
36 | 3.39M | } |
37 | 0 | return i; |
38 | 274k | } |
39 | | |
40 | 274k | void MoveToFront(std::vector<uint8_t>* v, size_t index) { |
41 | 274k | uint8_t value = (*v)[index]; |
42 | 3.39M | for (size_t i = index; i != 0; --i) { |
43 | 3.11M | (*v)[i] = (*v)[i - 1]; |
44 | 3.11M | } |
45 | 274k | (*v)[0] = value; |
46 | 274k | } |
47 | | |
48 | 1.01k | std::vector<uint8_t> MoveToFrontTransform(const std::vector<uint8_t>& v) { |
49 | 1.01k | if (v.empty()) return v; |
50 | 1.01k | uint8_t max_value = *std::max_element(v.begin(), v.end()); |
51 | 1.01k | std::vector<uint8_t> mtf(max_value + 1); |
52 | 21.4k | for (size_t i = 0; i <= max_value; ++i) mtf[i] = i; |
53 | 1.01k | std::vector<uint8_t> result(v.size()); |
54 | 275k | for (size_t i = 0; i < v.size(); ++i) { |
55 | 274k | size_t index = IndexOf(mtf, v[i]); |
56 | 274k | JXL_DASSERT(index < mtf.size()); |
57 | 274k | result[i] = static_cast<uint8_t>(index); |
58 | 274k | MoveToFront(&mtf, index); |
59 | 274k | } |
60 | 1.01k | return result; |
61 | 1.01k | } |
62 | | |
63 | | } // namespace |
64 | | |
65 | | Status EncodeContextMap(const std::vector<uint8_t>& context_map, |
66 | | size_t num_histograms, BitWriter* writer, |
67 | 1.27k | LayerType layer, AuxOut* aux_out) { |
68 | 1.27k | if (num_histograms == 1) { |
69 | | // Simple code |
70 | 266 | writer->Write(1, 1); |
71 | | // 0 bits per entry. |
72 | 266 | writer->Write(2, 0); |
73 | 266 | return true; |
74 | 266 | } |
75 | | |
76 | 1.01k | JxlMemoryManager* memory_manager = writer->memory_manager(); |
77 | 1.01k | std::vector<uint8_t> transformed_symbols = MoveToFrontTransform(context_map); |
78 | 1.01k | std::vector<std::vector<Token>> tokens(1); |
79 | 1.01k | std::vector<std::vector<Token>> mtf_tokens(1); |
80 | 274k | for (const uint8_t& ctx : context_map) { |
81 | 274k | tokens[0].emplace_back(0, ctx); |
82 | 274k | } |
83 | 274k | for (const uint8_t& sym : transformed_symbols) { |
84 | 274k | mtf_tokens[0].emplace_back(0, sym); |
85 | 274k | } |
86 | 1.01k | HistogramParams params; |
87 | 1.01k | params.uint_method = HistogramParams::HybridUintMethod::kContextMap; |
88 | 1.01k | size_t ans_cost; |
89 | 1.01k | size_t mtf_cost; |
90 | 1.01k | { |
91 | 1.01k | EntropyEncodingData codes; |
92 | 1.01k | JXL_ASSIGN_OR_RETURN( |
93 | 1.01k | ans_cost, BuildAndEncodeHistograms(memory_manager, params, 1, tokens, |
94 | 1.01k | &codes, nullptr, LayerType::Header, |
95 | 1.01k | /*aux_out*/ nullptr)); |
96 | 1.01k | } |
97 | 0 | { |
98 | 1.01k | EntropyEncodingData codes; |
99 | 1.01k | JXL_ASSIGN_OR_RETURN( |
100 | 1.01k | mtf_cost, BuildAndEncodeHistograms( |
101 | 1.01k | memory_manager, params, 1, mtf_tokens, &codes, nullptr, |
102 | 1.01k | LayerType::Header, /*aux_out*/ nullptr)); |
103 | 1.01k | } |
104 | 0 | bool use_mtf = mtf_cost < ans_cost; |
105 | | // Rebuild token list. |
106 | 1.01k | tokens[0].clear(); |
107 | 275k | for (size_t i = 0; i < transformed_symbols.size(); i++) { |
108 | 274k | tokens[0].emplace_back(0, |
109 | 274k | use_mtf ? transformed_symbols[i] : context_map[i]); |
110 | 274k | } |
111 | 1.01k | size_t entry_bits = CeilLog2Nonzero(num_histograms); |
112 | 1.01k | size_t simple_cost = entry_bits * context_map.size(); |
113 | 1.01k | if (entry_bits < 4 && simple_cost < ans_cost && simple_cost < mtf_cost) { |
114 | 489 | JXL_RETURN_IF_ERROR(writer->WithMaxBits( |
115 | 489 | 3 + entry_bits * context_map.size(), layer, aux_out, [&] { |
116 | 489 | writer->Write(1, 1); |
117 | 489 | writer->Write(2, entry_bits); |
118 | 489 | for (uint8_t entry : context_map) { |
119 | 489 | writer->Write(entry_bits, entry); |
120 | 489 | } |
121 | 489 | return true; |
122 | 489 | })); |
123 | 523 | } else { |
124 | 523 | JXL_RETURN_IF_ERROR(writer->WithMaxBits( |
125 | 523 | 2 + tokens[0].size() * 24, layer, aux_out, [&]() -> Status { |
126 | 523 | writer->Write(1, 0); |
127 | 523 | writer->Write(1, TO_JXL_BOOL(use_mtf)); // Use/don't use MTF. |
128 | 523 | EntropyEncodingData codes; |
129 | 523 | JXL_ASSIGN_OR_RETURN( |
130 | 523 | size_t cost, |
131 | 523 | BuildAndEncodeHistograms(memory_manager, params, 1, tokens, |
132 | 523 | &codes, writer, layer, aux_out)); |
133 | 523 | (void)cost; |
134 | 523 | WriteTokens(tokens[0], codes, 0, writer); |
135 | 523 | return true; |
136 | 523 | })); |
137 | 523 | } |
138 | 1.01k | return true; |
139 | 1.01k | } |
140 | | |
141 | | Status EncodeBlockCtxMap(const BlockCtxMap& block_ctx_map, BitWriter* writer, |
142 | 186 | AuxOut* aux_out) { |
143 | 186 | const auto& dct = block_ctx_map.dc_thresholds; |
144 | 186 | const auto& qft = block_ctx_map.qf_thresholds; |
145 | 186 | const auto& ctx_map = block_ctx_map.ctx_map; |
146 | 186 | return writer->WithMaxBits( |
147 | 186 | (dct[0].size() + dct[1].size() + dct[2].size() + qft.size()) * 34 + 1 + |
148 | 186 | 4 + 4 + ctx_map.size() * 10 + 1024, |
149 | 186 | LayerType::Ac, aux_out, [&]() -> Status { |
150 | 186 | if (dct[0].empty() && dct[1].empty() && dct[2].empty() && qft.empty() && |
151 | 186 | ctx_map.size() == 21 && |
152 | 186 | std::equal(ctx_map.begin(), ctx_map.end(), |
153 | 0 | BlockCtxMap::kDefaultCtxMap)) { |
154 | 0 | writer->Write(1, 1); // default |
155 | 0 | return true; |
156 | 0 | } |
157 | 186 | writer->Write(1, 0); |
158 | 558 | for (int j : {0, 1, 2}) { |
159 | 558 | writer->Write(4, dct[j].size()); |
160 | 558 | for (int i : dct[j]) { |
161 | 0 | JXL_RETURN_IF_ERROR( |
162 | 0 | U32Coder::Write(kDCThresholdDist, PackSigned(i), writer)); |
163 | 0 | } |
164 | 558 | } |
165 | 186 | writer->Write(4, qft.size()); |
166 | 186 | for (uint32_t i : qft) { |
167 | 1 | JXL_RETURN_IF_ERROR(U32Coder::Write(kQFThresholdDist, i - 1, writer)); |
168 | 1 | } |
169 | 186 | JXL_RETURN_IF_ERROR(EncodeContextMap(ctx_map, block_ctx_map.num_ctxs, |
170 | 186 | writer, LayerType::Ac, aux_out)); |
171 | 186 | return true; |
172 | 186 | }); |
173 | 186 | } |
174 | | |
175 | | } // namespace jxl |