Coverage Report

Created: 2025-11-14 07:32

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libjxl/lib/jxl/enc_context_map.cc
Line
Count
Source
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
5.18M
size_t IndexOf(const std::vector<uint8_t>& v, uint8_t value) {
33
5.18M
  size_t i = 0;
34
31.8M
  for (; i < v.size(); ++i) {
35
31.8M
    if (v[i] == value) return i;
36
31.8M
  }
37
0
  return i;
38
5.18M
}
39
40
5.18M
void MoveToFront(std::vector<uint8_t>* v, size_t index) {
41
5.18M
  uint8_t value = (*v)[index];
42
31.8M
  for (size_t i = index; i != 0; --i) {
43
26.6M
    (*v)[i] = (*v)[i - 1];
44
26.6M
  }
45
5.18M
  (*v)[0] = value;
46
5.18M
}
47
48
11.1k
std::vector<uint8_t> MoveToFrontTransform(const std::vector<uint8_t>& v) {
49
11.1k
  if (v.empty()) return v;
50
11.1k
  uint8_t max_value = *std::max_element(v.begin(), v.end());
51
11.1k
  std::vector<uint8_t> mtf(max_value + 1);
52
209k
  for (size_t i = 0; i <= max_value; ++i) mtf[i] = i;
53
11.1k
  std::vector<uint8_t> result(v.size());
54
5.19M
  for (size_t i = 0; i < v.size(); ++i) {
55
5.18M
    size_t index = IndexOf(mtf, v[i]);
56
5.18M
    JXL_DASSERT(index < mtf.size());
57
5.18M
    result[i] = static_cast<uint8_t>(index);
58
5.18M
    MoveToFront(&mtf, index);
59
5.18M
  }
60
11.1k
  return result;
61
11.1k
}
62
63
}  // namespace
64
65
Status EncodeContextMap(const std::vector<uint8_t>& context_map,
66
                        size_t num_histograms, BitWriter* writer,
67
17.3k
                        LayerType layer, AuxOut* aux_out) {
68
17.3k
  if (num_histograms == 1) {
69
    // Simple code
70
6.19k
    writer->Write(1, 1);
71
    // 0 bits per entry.
72
6.19k
    writer->Write(2, 0);
73
6.19k
    return true;
74
6.19k
  }
75
76
11.1k
  JxlMemoryManager* memory_manager = writer->memory_manager();
77
11.1k
  std::vector<uint8_t> transformed_symbols = MoveToFrontTransform(context_map);
78
11.1k
  std::vector<std::vector<Token>> tokens(1);
79
11.1k
  std::vector<std::vector<Token>> mtf_tokens(1);
80
5.18M
  for (const uint8_t& ctx : context_map) {
81
5.18M
    tokens[0].emplace_back(0, ctx);
82
5.18M
  }
83
5.18M
  for (const uint8_t& sym : transformed_symbols) {
84
5.18M
    mtf_tokens[0].emplace_back(0, sym);
85
5.18M
  }
86
11.1k
  HistogramParams params;
87
11.1k
  params.uint_method = HistogramParams::HybridUintMethod::kContextMap;
88
11.1k
  size_t ans_cost;
89
11.1k
  size_t mtf_cost;
90
11.1k
  {
91
11.1k
    EntropyEncodingData codes;
92
11.1k
    JXL_ASSIGN_OR_RETURN(
93
11.1k
        ans_cost, BuildAndEncodeHistograms(memory_manager, params, 1, tokens,
94
11.1k
                                           &codes, nullptr, LayerType::Header,
95
11.1k
                                           /*aux_out*/ nullptr));
96
11.1k
  }
97
0
  {
98
11.1k
    EntropyEncodingData codes;
99
11.1k
    JXL_ASSIGN_OR_RETURN(
100
11.1k
        mtf_cost, BuildAndEncodeHistograms(
101
11.1k
                      memory_manager, params, 1, mtf_tokens, &codes, nullptr,
102
11.1k
                      LayerType::Header, /*aux_out*/ nullptr));
103
11.1k
  }
104
0
  bool use_mtf = mtf_cost < ans_cost;
105
  // Rebuild token list.
106
11.1k
  tokens[0].clear();
107
5.19M
  for (size_t i = 0; i < transformed_symbols.size(); i++) {
108
5.18M
    tokens[0].emplace_back(0,
109
5.18M
                           use_mtf ? transformed_symbols[i] : context_map[i]);
110
5.18M
  }
111
11.1k
  size_t entry_bits = CeilLog2Nonzero(num_histograms);
112
11.1k
  size_t simple_cost = entry_bits * context_map.size();
113
11.1k
  if (entry_bits < 4 && simple_cost < ans_cost && simple_cost < mtf_cost) {
114
5.08k
    JXL_RETURN_IF_ERROR(writer->WithMaxBits(
115
5.08k
        3 + entry_bits * context_map.size(), layer, aux_out, [&] {
116
5.08k
          writer->Write(1, 1);
117
5.08k
          writer->Write(2, entry_bits);
118
5.08k
          for (uint8_t entry : context_map) {
119
5.08k
            writer->Write(entry_bits, entry);
120
5.08k
          }
121
5.08k
          return true;
122
5.08k
        }));
123
6.05k
  } else {
124
6.05k
    JXL_RETURN_IF_ERROR(writer->WithMaxBits(
125
6.05k
        2 + tokens[0].size() * 24, layer, aux_out, [&]() -> Status {
126
6.05k
          writer->Write(1, 0);
127
6.05k
          writer->Write(1, TO_JXL_BOOL(use_mtf));  // Use/don't use MTF.
128
6.05k
          EntropyEncodingData codes;
129
6.05k
          JXL_ASSIGN_OR_RETURN(
130
6.05k
              size_t cost,
131
6.05k
              BuildAndEncodeHistograms(memory_manager, params, 1, tokens,
132
6.05k
                                       &codes, writer, layer, aux_out));
133
6.05k
          (void)cost;
134
6.05k
          WriteTokens(tokens[0], codes, 0, writer);
135
6.05k
          return true;
136
6.05k
        }));
137
6.05k
  }
138
11.1k
  return true;
139
11.1k
}
140
141
Status EncodeBlockCtxMap(const BlockCtxMap& block_ctx_map, BitWriter* writer,
142
3.02k
                         AuxOut* aux_out) {
143
3.02k
  const auto& dct = block_ctx_map.dc_thresholds;
144
3.02k
  const auto& qft = block_ctx_map.qf_thresholds;
145
3.02k
  const auto& ctx_map = block_ctx_map.ctx_map;
146
3.02k
  return writer->WithMaxBits(
147
3.02k
      (dct[0].size() + dct[1].size() + dct[2].size() + qft.size()) * 34 + 1 +
148
3.02k
          4 + 4 + ctx_map.size() * 10 + 1024,
149
3.02k
      LayerType::Ac, aux_out, [&]() -> Status {
150
3.02k
        if (dct[0].empty() && dct[1].empty() && dct[2].empty() && qft.empty() &&
151
2.90k
            ctx_map.size() == 21 &&
152
0
            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
3.02k
        writer->Write(1, 0);
158
9.06k
        for (int j : {0, 1, 2}) {
159
9.06k
          writer->Write(4, dct[j].size());
160
9.06k
          for (int i : dct[j]) {
161
0
            JXL_RETURN_IF_ERROR(
162
0
                U32Coder::Write(kDCThresholdDist, PackSigned(i), writer));
163
0
          }
164
9.06k
        }
165
3.02k
        writer->Write(4, qft.size());
166
3.02k
        for (uint32_t i : qft) {
167
118
          JXL_RETURN_IF_ERROR(U32Coder::Write(kQFThresholdDist, i - 1, writer));
168
118
        }
169
3.02k
        JXL_RETURN_IF_ERROR(EncodeContextMap(ctx_map, block_ctx_map.num_ctxs,
170
3.02k
                                             writer, LayerType::Ac, aux_out));
171
3.02k
        return true;
172
3.02k
      });
173
3.02k
}
174
175
}  // namespace jxl