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