/src/libjxl/lib/jxl/modular/encoding/dec_ma.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/modular/encoding/dec_ma.h" |
7 | | |
8 | | #include <jxl/memory_manager.h> |
9 | | |
10 | | #include <limits> |
11 | | |
12 | | #include "lib/jxl/base/printf_macros.h" |
13 | | #include "lib/jxl/base/status.h" |
14 | | #include "lib/jxl/dec_ans.h" |
15 | | #include "lib/jxl/modular/encoding/ma_common.h" |
16 | | #include "lib/jxl/modular/modular_image.h" |
17 | | #include "lib/jxl/pack_signed.h" |
18 | | |
19 | | namespace jxl { |
20 | | |
21 | | namespace { |
22 | | |
23 | | Status ValidateTree( |
24 | | const Tree &tree, |
25 | | const std::vector<std::pair<pixel_type, pixel_type>> &prop_bounds, |
26 | 154k | size_t root) { |
27 | 154k | if (tree[root].property == -1) return true; |
28 | 46.9k | size_t p = tree[root].property; |
29 | 46.9k | int val = tree[root].splitval; |
30 | 46.9k | if (prop_bounds[p].first > val) return JXL_FAILURE("Invalid tree"); |
31 | | // Splitting at max value makes no sense: left range will be exactly same |
32 | | // as parent, right range will be invalid (min > max). |
33 | 46.9k | if (prop_bounds[p].second <= val) return JXL_FAILURE("Invalid tree"); |
34 | 46.9k | auto new_bounds = prop_bounds; |
35 | 46.9k | new_bounds[p].first = val + 1; |
36 | 46.9k | JXL_RETURN_IF_ERROR(ValidateTree(tree, new_bounds, tree[root].lchild)); |
37 | 46.9k | new_bounds[p] = prop_bounds[p]; |
38 | 46.9k | new_bounds[p].second = val; |
39 | 46.9k | return ValidateTree(tree, new_bounds, tree[root].rchild); |
40 | 46.9k | } |
41 | | |
42 | | Status DecodeTree(BitReader *br, ANSSymbolReader *reader, |
43 | | const std::vector<uint8_t> &context_map, Tree *tree, |
44 | 60.5k | size_t tree_size_limit) { |
45 | 60.5k | size_t leaf_id = 0; |
46 | 60.5k | size_t to_decode = 1; |
47 | 60.5k | tree->clear(); |
48 | 502k | while (to_decode > 0) { |
49 | 442k | JXL_RETURN_IF_ERROR(br->AllReadsWithinBounds()); |
50 | 442k | if (tree->size() > tree_size_limit) { |
51 | 9 | return JXL_FAILURE("Tree is too large: %" PRIuS " nodes vs %" PRIuS |
52 | 9 | " max nodes", |
53 | 9 | tree->size(), tree_size_limit); |
54 | 9 | } |
55 | 442k | to_decode--; |
56 | 442k | uint32_t prop1 = reader->ReadHybridUint(kPropertyContext, br, context_map); |
57 | 442k | if (prop1 > 256) return JXL_FAILURE("Invalid tree property value"); |
58 | 442k | int property = prop1 - 1; |
59 | 442k | if (property == -1) { |
60 | 108k | size_t predictor = |
61 | 108k | reader->ReadHybridUint(kPredictorContext, br, context_map); |
62 | 108k | if (predictor >= kNumModularPredictors) { |
63 | 11 | return JXL_FAILURE("Invalid predictor"); |
64 | 11 | } |
65 | 108k | int64_t predictor_offset = |
66 | 108k | UnpackSigned(reader->ReadHybridUint(kOffsetContext, br, context_map)); |
67 | 108k | uint32_t mul_log = |
68 | 108k | reader->ReadHybridUint(kMultiplierLogContext, br, context_map); |
69 | 108k | if (mul_log >= 31) { |
70 | 4 | return JXL_FAILURE("Invalid multiplier logarithm"); |
71 | 4 | } |
72 | 108k | uint32_t mul_bits = |
73 | 108k | reader->ReadHybridUint(kMultiplierBitsContext, br, context_map); |
74 | 108k | if (mul_bits >= (1u << (31u - mul_log)) - 1u) { |
75 | 2 | return JXL_FAILURE("Invalid multiplier"); |
76 | 2 | } |
77 | 108k | uint32_t multiplier = (mul_bits + 1U) << mul_log; |
78 | 108k | tree->emplace_back(-1, 0, leaf_id++, 0, static_cast<Predictor>(predictor), |
79 | 108k | predictor_offset, multiplier); |
80 | 108k | continue; |
81 | 108k | } |
82 | 333k | int splitval = |
83 | 333k | UnpackSigned(reader->ReadHybridUint(kSplitValContext, br, context_map)); |
84 | 333k | tree->emplace_back(property, splitval, tree->size() + to_decode + 1, |
85 | 333k | tree->size() + to_decode + 2, Predictor::Zero, 0, 1); |
86 | 333k | to_decode += 2; |
87 | 333k | } |
88 | 60.3k | std::vector<std::pair<pixel_type, pixel_type>> prop_bounds; |
89 | 60.3k | prop_bounds.resize(256, {std::numeric_limits<pixel_type>::min(), |
90 | 60.3k | std::numeric_limits<pixel_type>::max()}); |
91 | 60.3k | return ValidateTree(*tree, prop_bounds, 0); |
92 | 60.5k | } |
93 | | } // namespace |
94 | | |
95 | | Status DecodeTree(JxlMemoryManager *memory_manager, BitReader *br, Tree *tree, |
96 | 61.1k | size_t tree_size_limit) { |
97 | 61.1k | std::vector<uint8_t> tree_context_map; |
98 | 61.1k | ANSCode tree_code; |
99 | 61.1k | JXL_RETURN_IF_ERROR(DecodeHistograms(memory_manager, br, kNumTreeContexts, |
100 | 61.1k | &tree_code, &tree_context_map)); |
101 | | // TODO(eustas): investigate more infinite tree cases. |
102 | 60.5k | if (tree_code.degenerate_symbols[tree_context_map[kPropertyContext]] > 0) { |
103 | 9 | return JXL_FAILURE("Infinite tree"); |
104 | 9 | } |
105 | 121k | JXL_ASSIGN_OR_RETURN(ANSSymbolReader reader, |
106 | 121k | ANSSymbolReader::Create(&tree_code, br)); |
107 | 121k | JXL_RETURN_IF_ERROR(DecodeTree(br, &reader, tree_context_map, tree, |
108 | 121k | std::min(tree_size_limit, kMaxTreeSize))); |
109 | 60.3k | if (!reader.CheckANSFinalState()) { |
110 | 0 | return JXL_FAILURE("ANS decode final state failed"); |
111 | 0 | } |
112 | 60.3k | return true; |
113 | 60.3k | } |
114 | | |
115 | | } // namespace jxl |