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