Coverage Report

Created: 2025-12-31 07:53

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libjxl/lib/jxl/coeff_order.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
#include "lib/jxl/coeff_order.h"
7
8
#include <jxl/memory_manager.h>
9
10
#include <algorithm>
11
#include <cstddef>
12
#include <cstdint>
13
#include <cstring>
14
#include <vector>
15
16
#include "lib/jxl/ac_strategy.h"
17
#include "lib/jxl/base/status.h"
18
#include "lib/jxl/coeff_order_fwd.h"
19
#include "lib/jxl/dec_ans.h"
20
#include "lib/jxl/dec_bit_reader.h"
21
#include "lib/jxl/frame_dimensions.h"
22
#include "lib/jxl/lehmer_code.h"
23
#include "lib/jxl/modular/encoding/encoding.h"
24
25
namespace jxl {
26
27
static_assert(AcStrategy::kNumValidStrategies == kStrategyOrder.size(),
28
              "Update this array when adding or removing AC strategies.");
29
30
1.79M
uint32_t CoeffOrderContext(uint32_t val) {
31
1.79M
  uint32_t token, nbits, bits;
32
1.79M
  HybridUintConfig(0, 0, 0).Encode(val, &token, &nbits, &bits);
33
1.79M
  return std::min(token, kPermutationContexts - 1);
34
1.79M
}
35
36
namespace {
37
Status ReadPermutation(size_t skip, size_t size, coeff_order_t* order,
38
                       BitReader* br, ANSSymbolReader* reader,
39
56.1k
                       const std::vector<uint8_t>& context_map) {
40
56.1k
  std::vector<LehmerT> lehmer(size);
41
  // temp space needs to be as large as the next power of 2, so doubling the
42
  // allocated size is enough.
43
56.1k
  std::vector<uint32_t> temp(size * 2);
44
56.1k
  uint32_t end =
45
56.1k
      reader->ReadHybridUint(CoeffOrderContext(size), br, context_map) + skip;
46
56.1k
  if (end > size) {
47
925
    return JXL_FAILURE("Invalid permutation size");
48
925
  }
49
55.1k
  uint32_t last = 0;
50
518k
  for (size_t i = skip; i < end; ++i) {
51
463k
    lehmer[i] =
52
463k
        reader->ReadHybridUint(CoeffOrderContext(last), br, context_map);
53
463k
    last = lehmer[i];
54
463k
    if (lehmer[i] >= size - i) {
55
147
      return JXL_FAILURE("Invalid lehmer code");
56
147
    }
57
463k
  }
58
55.0k
  if (order == nullptr) return true;
59
38.8k
  JXL_RETURN_IF_ERROR(
60
38.8k
      DecodeLehmerCode(lehmer.data(), temp.data(), size, order));
61
38.8k
  return true;
62
38.8k
}
63
64
}  // namespace
65
66
Status DecodePermutation(JxlMemoryManager* memory_manager, size_t skip,
67
30.3k
                         size_t size, coeff_order_t* order, BitReader* br) {
68
30.3k
  std::vector<uint8_t> context_map;
69
30.3k
  ANSCode code;
70
30.3k
  JXL_RETURN_IF_ERROR(DecodeHistograms(memory_manager, br, kPermutationContexts,
71
30.3k
                                       &code, &context_map));
72
48.0k
  JXL_ASSIGN_OR_RETURN(ANSSymbolReader reader,
73
48.0k
                       ANSSymbolReader::Create(&code, br));
74
48.0k
  JXL_RETURN_IF_ERROR(
75
48.0k
      ReadPermutation(skip, size, order, br, &reader, context_map));
76
23.0k
  if (!reader.CheckANSFinalState()) {
77
0
    return JXL_FAILURE("Invalid ANS stream");
78
0
  }
79
23.0k
  return true;
80
23.0k
}
81
82
namespace {
83
84
Status DecodeCoeffOrder(AcStrategy acs, coeff_order_t* order, BitReader* br,
85
                        ANSSymbolReader* reader,
86
                        std::vector<coeff_order_t>& natural_order,
87
32.0k
                        const std::vector<uint8_t>& context_map) {
88
32.0k
  const size_t llf = acs.covered_blocks_x() * acs.covered_blocks_y();
89
32.0k
  const size_t size = kDCTBlockSize * llf;
90
91
32.0k
  JXL_RETURN_IF_ERROR(
92
32.0k
      ReadPermutation(llf, size, order, br, reader, context_map));
93
32.0k
  if (order == nullptr) return true;
94
3.71M
  for (size_t k = 0; k < size; ++k) {
95
3.70M
    order[k] = natural_order[order[k]];
96
3.70M
  }
97
15.7k
  return true;
98
32.0k
}
99
100
}  // namespace
101
102
Status DecodeCoeffOrders(JxlMemoryManager* memory_manager, uint16_t used_orders,
103
                         uint32_t used_acs, coeff_order_t* order,
104
4.30k
                         BitReader* br) {
105
4.30k
  uint16_t computed = 0;
106
4.30k
  std::vector<uint8_t> context_map;
107
4.30k
  ANSCode code;
108
4.30k
  ANSSymbolReader reader;
109
4.30k
  std::vector<coeff_order_t> natural_order;
110
  // Bitstream does not have histograms if no coefficient order is used.
111
4.30k
  if (used_orders != 0) {
112
2.58k
    JXL_RETURN_IF_ERROR(DecodeHistograms(
113
2.58k
        memory_manager, br, kPermutationContexts, &code, &context_map));
114
4.95k
    JXL_ASSIGN_OR_RETURN(reader, ANSSymbolReader::Create(&code, br));
115
4.95k
  }
116
4.20k
  uint32_t acs_mask = 0;
117
117k
  for (uint8_t o = 0; o < AcStrategy::kNumValidStrategies; ++o) {
118
113k
    if ((used_acs & (1 << o)) == 0) continue;
119
17.6k
    acs_mask |= 1 << kStrategyOrder[o];
120
17.6k
  }
121
115k
  for (uint8_t o = 0; o < AcStrategy::kNumValidStrategies; ++o) {
122
111k
    uint8_t ord = kStrategyOrder[o];
123
111k
    if (computed & (1 << ord)) continue;
124
53.8k
    computed |= 1 << ord;
125
53.8k
    AcStrategy acs = AcStrategy::FromRawStrategy(o);
126
53.8k
    bool used = (acs_mask & (1 << ord)) != 0;
127
128
53.8k
    const size_t llf = acs.covered_blocks_x() * acs.covered_blocks_y();
129
53.8k
    const size_t size = kDCTBlockSize * llf;
130
131
53.8k
    if (used || (used_orders & (1 << ord))) {
132
15.5k
      if (natural_order.size() < size) natural_order.resize(size);
133
15.5k
      acs.ComputeNaturalCoeffOrder(natural_order.data());
134
15.5k
    }
135
136
53.8k
    if ((used_orders & (1 << ord)) == 0) {
137
      // No need to set the default order if no ACS uses this order.
138
43.1k
      if (used) {
139
19.3k
        for (size_t c = 0; c < 3; c++) {
140
14.5k
          memcpy(&order[CoeffOrderOffset(ord, c)], natural_order.data(),
141
14.5k
                 size * sizeof(*order));
142
14.5k
        }
143
4.84k
      }
144
43.1k
    } else {
145
42.7k
      for (size_t c = 0; c < 3; c++) {
146
32.0k
        coeff_order_t* dest = used ? &order[CoeffOrderOffset(ord, c)] : nullptr;
147
32.0k
        JXL_RETURN_IF_ERROR(DecodeCoeffOrder(acs, dest, br, &reader,
148
32.0k
                                             natural_order, context_map));
149
32.0k
      }
150
10.7k
    }
151
53.8k
  }
152
4.12k
  if (used_orders && !reader.CheckANSFinalState()) {
153
0
    return JXL_FAILURE("Invalid ANS stream");
154
0
  }
155
4.12k
  return true;
156
4.12k
}
157
158
}  // namespace jxl