/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 <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 | 120k | uint32_t CoeffOrderContext(uint32_t val) { |
31 | 120k | uint32_t token, nbits, bits; |
32 | 120k | HybridUintConfig(0, 0, 0).Encode(val, &token, &nbits, &bits); |
33 | 120k | return std::min(token, kPermutationContexts - 1); |
34 | 120k | } |
35 | | |
36 | | namespace { |
37 | | Status ReadPermutation(size_t skip, size_t size, coeff_order_t* order, |
38 | | BitReader* br, ANSSymbolReader* reader, |
39 | 14.2k | const std::vector<uint8_t>& context_map) { |
40 | 14.2k | 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 | 14.2k | std::vector<uint32_t> temp(size * 2); |
44 | 14.2k | uint32_t end = |
45 | 14.2k | reader->ReadHybridUint(CoeffOrderContext(size), br, context_map) + skip; |
46 | 14.2k | if (end > size) { |
47 | 184 | return JXL_FAILURE("Invalid permutation size"); |
48 | 184 | } |
49 | 14.0k | uint32_t last = 0; |
50 | 37.7k | for (size_t i = skip; i < end; ++i) { |
51 | 23.8k | lehmer[i] = |
52 | 23.8k | reader->ReadHybridUint(CoeffOrderContext(last), br, context_map); |
53 | 23.8k | last = lehmer[i]; |
54 | 23.8k | if (lehmer[i] >= size - i) { |
55 | 73 | return JXL_FAILURE("Invalid lehmer code"); |
56 | 73 | } |
57 | 23.8k | } |
58 | 13.9k | if (order == nullptr) return true; |
59 | 9.73k | JXL_RETURN_IF_ERROR( |
60 | 9.73k | DecodeLehmerCode(lehmer.data(), temp.data(), size, order)); |
61 | 9.73k | return true; |
62 | 9.73k | } |
63 | | |
64 | | } // namespace |
65 | | |
66 | | Status DecodePermutation(JxlMemoryManager* memory_manager, size_t skip, |
67 | 10.0k | size_t size, coeff_order_t* order, BitReader* br) { |
68 | 10.0k | std::vector<uint8_t> context_map; |
69 | 10.0k | ANSCode code; |
70 | 10.0k | JXL_RETURN_IF_ERROR(DecodeHistograms(memory_manager, br, kPermutationContexts, |
71 | 10.0k | &code, &context_map)); |
72 | 17.7k | JXL_ASSIGN_OR_RETURN(ANSSymbolReader reader, |
73 | 17.7k | ANSSymbolReader::Create(&code, br)); |
74 | 17.7k | JXL_RETURN_IF_ERROR( |
75 | 17.7k | ReadPermutation(skip, size, order, br, &reader, context_map)); |
76 | 8.64k | if (!reader.CheckANSFinalState()) { |
77 | 0 | return JXL_FAILURE("Invalid ANS stream"); |
78 | 0 | } |
79 | 8.64k | return true; |
80 | 8.64k | } |
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 | 5.31k | const std::vector<uint8_t>& context_map) { |
88 | 5.31k | const size_t llf = acs.covered_blocks_x() * acs.covered_blocks_y(); |
89 | 5.31k | const size_t size = kDCTBlockSize * llf; |
90 | | |
91 | 5.31k | JXL_RETURN_IF_ERROR( |
92 | 5.31k | ReadPermutation(llf, size, order, br, reader, context_map)); |
93 | 5.30k | if (order == nullptr) return true; |
94 | 92.9k | for (size_t k = 0; k < size; ++k) { |
95 | 91.9k | order[k] = natural_order[order[k]]; |
96 | 91.9k | } |
97 | 1.08k | return true; |
98 | 5.30k | } |
99 | | |
100 | | } // namespace |
101 | | |
102 | | Status DecodeCoeffOrders(JxlMemoryManager* memory_manager, uint16_t used_orders, |
103 | | uint32_t used_acs, coeff_order_t* order, |
104 | 1.82k | BitReader* br) { |
105 | 1.82k | uint16_t computed = 0; |
106 | 1.82k | std::vector<uint8_t> context_map; |
107 | 1.82k | ANSCode code; |
108 | 1.82k | ANSSymbolReader reader; |
109 | 1.82k | std::vector<coeff_order_t> natural_order; |
110 | | // Bitstream does not have histograms if no coefficient order is used. |
111 | 1.82k | if (used_orders != 0) { |
112 | 344 | JXL_RETURN_IF_ERROR(DecodeHistograms( |
113 | 344 | memory_manager, br, kPermutationContexts, &code, &context_map)); |
114 | 626 | JXL_ASSIGN_OR_RETURN(reader, ANSSymbolReader::Create(&code, br)); |
115 | 626 | } |
116 | 1.79k | uint32_t acs_mask = 0; |
117 | 50.2k | for (uint8_t o = 0; o < AcStrategy::kNumValidStrategies; ++o) { |
118 | 48.4k | if ((used_acs & (1 << o)) == 0) continue; |
119 | 1.95k | acs_mask |= 1 << kStrategyOrder[o]; |
120 | 1.95k | } |
121 | 49.9k | for (uint8_t o = 0; o < AcStrategy::kNumValidStrategies; ++o) { |
122 | 48.1k | uint8_t ord = kStrategyOrder[o]; |
123 | 48.1k | if (computed & (1 << ord)) continue; |
124 | 23.1k | computed |= 1 << ord; |
125 | 23.1k | AcStrategy acs = AcStrategy::FromRawStrategy(o); |
126 | 23.1k | bool used = (acs_mask & (1 << ord)) != 0; |
127 | | |
128 | 23.1k | const size_t llf = acs.covered_blocks_x() * acs.covered_blocks_y(); |
129 | 23.1k | const size_t size = kDCTBlockSize * llf; |
130 | | |
131 | 23.1k | if (used || (used_orders & (1 << ord))) { |
132 | 3.34k | if (natural_order.size() < size) natural_order.resize(size); |
133 | 3.34k | acs.ComputeNaturalCoeffOrder(natural_order.data()); |
134 | 3.34k | } |
135 | | |
136 | 23.1k | if ((used_orders & (1 << ord)) == 0) { |
137 | | // No need to set the default order if no ACS uses this order. |
138 | 21.4k | if (used) { |
139 | 6.27k | for (size_t c = 0; c < 3; c++) { |
140 | 4.70k | memcpy(&order[CoeffOrderOffset(ord, c)], natural_order.data(), |
141 | 4.70k | size * sizeof(*order)); |
142 | 4.70k | } |
143 | 1.56k | } |
144 | 21.4k | } else { |
145 | 7.07k | for (size_t c = 0; c < 3; c++) { |
146 | 5.31k | coeff_order_t* dest = used ? &order[CoeffOrderOffset(ord, c)] : nullptr; |
147 | 5.31k | JXL_RETURN_IF_ERROR(DecodeCoeffOrder(acs, dest, br, &reader, |
148 | 5.31k | natural_order, context_map)); |
149 | 5.31k | } |
150 | 1.77k | } |
151 | 23.1k | } |
152 | 1.77k | if (used_orders && !reader.CheckANSFinalState()) { |
153 | 0 | return JXL_FAILURE("Invalid ANS stream"); |
154 | 0 | } |
155 | 1.77k | return true; |
156 | 1.77k | } |
157 | | |
158 | | } // namespace jxl |