/src/libjxl/lib/jxl/modular/encoding/dec_ma.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/modular/encoding/dec_ma.h" |
7 | | |
8 | | #include <jxl/memory_manager.h> |
9 | | |
10 | | #include <algorithm> |
11 | | #include <cstddef> |
12 | | #include <cstdint> |
13 | | #include <limits> |
14 | | #include <utility> |
15 | | #include <vector> |
16 | | |
17 | | #include "lib/jxl/base/printf_macros.h" |
18 | | #include "lib/jxl/base/status.h" |
19 | | #include "lib/jxl/dec_ans.h" |
20 | | #include "lib/jxl/dec_bit_reader.h" |
21 | | #include "lib/jxl/modular/encoding/ma_common.h" |
22 | | #include "lib/jxl/modular/modular_image.h" |
23 | | #include "lib/jxl/modular/options.h" |
24 | | #include "lib/jxl/pack_signed.h" |
25 | | |
26 | | namespace jxl { |
27 | | |
28 | | namespace { |
29 | | |
30 | | enum class NextAction { CHECK_AND_GO_LEFT, GO_RIGHT, POP }; |
31 | | |
32 | | struct WorkItem { |
33 | | size_t node_index; |
34 | | pixel_type orig_l; |
35 | | pixel_type orig_u; |
36 | | NextAction action; |
37 | | }; |
38 | | |
39 | 40.1k | Status ValidateTree(const Tree& tree) { |
40 | 40.1k | if (tree.empty()) return true; |
41 | | // TODO(eustas): or invalid? |
42 | | |
43 | 40.1k | int num_properties = 0; |
44 | 186k | for (auto node : tree) { |
45 | 186k | if (node.property >= num_properties) { |
46 | 4.54k | num_properties = node.property + 1; |
47 | 4.54k | } |
48 | 186k | } |
49 | | |
50 | 40.1k | std::vector<std::pair<pixel_type, pixel_type>> property_ranges( |
51 | 40.1k | num_properties); |
52 | 77.2k | for (int i = 0; i < num_properties; i++) { |
53 | 37.0k | property_ranges[i].first = std::numeric_limits<pixel_type>::min(); |
54 | 37.0k | property_ranges[i].second = std::numeric_limits<pixel_type>::max(); |
55 | 37.0k | } |
56 | | |
57 | 40.1k | constexpr size_t kHeightLimit = 2048; |
58 | | |
59 | 40.1k | std::vector<WorkItem> stack; |
60 | 40.1k | stack.push_back({/*node_index=*/0, /*orig_l=*/0, /*orig_u=*/0, |
61 | 40.1k | NextAction::CHECK_AND_GO_LEFT}); |
62 | | |
63 | 277k | while (!stack.empty()) { |
64 | 237k | if (stack.size() >= kHeightLimit) return JXL_FAILURE("Tree too tall"); |
65 | 237k | WorkItem& item = stack.back(); |
66 | 237k | const auto& node = tree[item.node_index]; |
67 | 237k | switch (item.action) { |
68 | 138k | case NextAction::CHECK_AND_GO_LEFT: { |
69 | 138k | int16_t p = node.property; |
70 | 138k | if (p == -1) { |
71 | 89.5k | stack.pop_back(); |
72 | 89.5k | continue; |
73 | 89.5k | } |
74 | 49.4k | PropertyVal v = node.splitval; |
75 | 49.4k | pixel_type l = property_ranges[p].first; |
76 | 49.4k | pixel_type u = property_ranges[p].second; |
77 | 49.4k | if (l > v || u <= v) { |
78 | 17 | return JXL_FAILURE("Invalid tree"); |
79 | 17 | } |
80 | 49.3k | item.orig_l = l; |
81 | 49.3k | item.orig_u = u; |
82 | 49.3k | item.action = NextAction::GO_RIGHT; |
83 | 49.3k | property_ranges[node.property].first = node.splitval + 1; |
84 | 49.3k | stack.push_back({/*node_index=*/node.lchild, |
85 | 49.3k | /*orig_l=*/0, /*orig_u=*/0, |
86 | 49.3k | NextAction::CHECK_AND_GO_LEFT}); |
87 | 49.3k | continue; |
88 | 49.4k | } |
89 | | |
90 | 49.3k | case NextAction::GO_RIGHT: |
91 | 49.3k | item.action = NextAction::POP; |
92 | 49.3k | property_ranges[node.property].first = item.orig_l; |
93 | 49.3k | property_ranges[node.property].second = node.splitval; |
94 | 49.3k | stack.push_back({/*node_index=*/node.rchild, |
95 | 49.3k | /*orig_l=*/0, /*orig_u=*/0, |
96 | 49.3k | NextAction::CHECK_AND_GO_LEFT}); |
97 | 49.3k | continue; |
98 | | |
99 | 49.3k | case NextAction::POP: |
100 | 49.3k | property_ranges[node.property].second = item.orig_u; |
101 | 49.3k | stack.pop_back(); |
102 | 49.3k | continue; |
103 | 237k | } |
104 | 237k | } |
105 | | |
106 | 40.1k | return true; |
107 | 40.1k | } |
108 | | |
109 | | Status DecodeTree(BitReader* br, ANSSymbolReader* reader, |
110 | | const std::vector<uint8_t>& context_map, Tree* tree, |
111 | 40.3k | size_t tree_size_limit) { |
112 | 40.3k | size_t leaf_id = 0; |
113 | 40.3k | size_t to_decode = 1; |
114 | 40.3k | tree->clear(); |
115 | 663k | while (to_decode > 0) { |
116 | 623k | JXL_RETURN_IF_ERROR(br->AllReadsWithinBounds()); |
117 | 622k | if (tree->size() > tree_size_limit) { |
118 | 16 | return JXL_FAILURE("Tree is too large: %" PRIuS " nodes vs %" PRIuS |
119 | 16 | " max nodes", |
120 | 16 | tree->size(), tree_size_limit); |
121 | 16 | } |
122 | 622k | to_decode--; |
123 | 622k | uint32_t prop1 = reader->ReadHybridUint(kPropertyContext, br, context_map); |
124 | 622k | if (prop1 > 256) return JXL_FAILURE("Invalid tree property value"); |
125 | 622k | int property = prop1 - 1; |
126 | 622k | if (property == -1) { |
127 | 176k | size_t predictor = |
128 | 176k | reader->ReadHybridUint(kPredictorContext, br, context_map); |
129 | 176k | if (predictor >= kNumModularPredictors) { |
130 | 7 | return JXL_FAILURE("Invalid predictor"); |
131 | 7 | } |
132 | 176k | int64_t predictor_offset = |
133 | 176k | UnpackSigned(reader->ReadHybridUint(kOffsetContext, br, context_map)); |
134 | 176k | uint32_t mul_log = |
135 | 176k | reader->ReadHybridUint(kMultiplierLogContext, br, context_map); |
136 | 176k | if (mul_log >= 31) { |
137 | 5 | return JXL_FAILURE("Invalid multiplier logarithm"); |
138 | 5 | } |
139 | 176k | uint32_t mul_bits = |
140 | 176k | reader->ReadHybridUint(kMultiplierBitsContext, br, context_map); |
141 | 176k | if (mul_bits >= (1u << (31u - mul_log)) - 1u) { |
142 | 1 | return JXL_FAILURE("Invalid multiplier"); |
143 | 1 | } |
144 | 176k | uint32_t multiplier = (mul_bits + 1U) << mul_log; |
145 | 176k | Predictor p = static_cast<Predictor>(static_cast<uint32_t>(predictor)); |
146 | 176k | tree->emplace_back(-1, 0, static_cast<int>(leaf_id), 0, p, |
147 | 176k | predictor_offset, multiplier); |
148 | 176k | leaf_id++; |
149 | 176k | continue; |
150 | 176k | } |
151 | 446k | int splitval = |
152 | 446k | UnpackSigned(reader->ReadHybridUint(kSplitValContext, br, context_map)); |
153 | 446k | tree->emplace_back( |
154 | 446k | property, splitval, static_cast<int>(tree->size() + to_decode + 1), |
155 | 446k | static_cast<int>(tree->size() + to_decode + 2), Predictor::Zero, 0, 1); |
156 | 446k | to_decode += 2; |
157 | 446k | } |
158 | 40.1k | return ValidateTree(*tree); |
159 | 40.3k | } |
160 | | } // namespace |
161 | | |
162 | | Status DecodeTree(JxlMemoryManager* memory_manager, BitReader* br, Tree* tree, |
163 | 40.8k | size_t tree_size_limit) { |
164 | 40.8k | std::vector<uint8_t> tree_context_map; |
165 | 40.8k | ANSCode tree_code; |
166 | 40.8k | JXL_RETURN_IF_ERROR(DecodeHistograms(memory_manager, br, kNumTreeContexts, |
167 | 40.8k | &tree_code, &tree_context_map)); |
168 | | // TODO(eustas): investigate more infinite tree cases. |
169 | 40.4k | if (tree_code.degenerate_symbols[tree_context_map[kPropertyContext]] > 0) { |
170 | 8 | return JXL_FAILURE("Infinite tree"); |
171 | 8 | } |
172 | 80.7k | JXL_ASSIGN_OR_RETURN(ANSSymbolReader reader, |
173 | 80.7k | ANSSymbolReader::Create(&tree_code, br)); |
174 | 80.7k | JXL_RETURN_IF_ERROR(DecodeTree(br, &reader, tree_context_map, tree, |
175 | 80.7k | std::min(tree_size_limit, kMaxTreeSize))); |
176 | 40.1k | if (!reader.CheckANSFinalState()) { |
177 | 0 | return JXL_FAILURE("ANS decode final state failed"); |
178 | 0 | } |
179 | 40.1k | return true; |
180 | 40.1k | } |
181 | | |
182 | | } // namespace jxl |