/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 <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 | 25.3k | Status ValidateTree(const Tree &tree) { |
31 | 25.3k | int num_properties = 0; |
32 | 36.5k | for (auto node : tree) { |
33 | 36.5k | if (node.property >= num_properties) { |
34 | 1.95k | num_properties = node.property + 1; |
35 | 1.95k | } |
36 | 36.5k | } |
37 | 25.3k | std::vector<int> height(tree.size()); |
38 | 25.3k | std::vector<std::pair<pixel_type, pixel_type>> property_ranges( |
39 | 25.3k | num_properties * tree.size()); |
40 | 41.2k | for (int i = 0; i < num_properties; i++) { |
41 | 15.8k | property_ranges[i].first = std::numeric_limits<pixel_type>::min(); |
42 | 15.8k | property_ranges[i].second = std::numeric_limits<pixel_type>::max(); |
43 | 15.8k | } |
44 | 25.3k | const int kHeightLimit = 2048; |
45 | 61.6k | for (size_t i = 0; i < tree.size(); i++) { |
46 | 36.2k | if (height[i] > kHeightLimit) { |
47 | 0 | return JXL_FAILURE("Tree too tall: %d", height[i]); |
48 | 0 | } |
49 | 36.2k | if (tree[i].property == -1) continue; |
50 | 5.43k | height[tree[i].lchild] = height[i] + 1; |
51 | 5.43k | height[tree[i].rchild] = height[i] + 1; |
52 | 88.3k | for (size_t p = 0; p < static_cast<size_t>(num_properties); p++) { |
53 | 82.9k | if (p == static_cast<size_t>(tree[i].property)) { |
54 | 5.43k | pixel_type l = property_ranges[i * num_properties + p].first; |
55 | 5.43k | pixel_type u = property_ranges[i * num_properties + p].second; |
56 | 5.43k | pixel_type val = tree[i].splitval; |
57 | 5.43k | if (l > val || u <= val) { |
58 | 8 | return JXL_FAILURE("Invalid tree"); |
59 | 8 | } |
60 | 5.42k | property_ranges[tree[i].lchild * num_properties + p] = |
61 | 5.42k | std::make_pair(val + 1, u); |
62 | 5.42k | property_ranges[tree[i].rchild * num_properties + p] = |
63 | 5.42k | std::make_pair(l, val); |
64 | 77.5k | } else { |
65 | 77.5k | property_ranges[tree[i].lchild * num_properties + p] = |
66 | 77.5k | property_ranges[i * num_properties + p]; |
67 | 77.5k | property_ranges[tree[i].rchild * num_properties + p] = |
68 | 77.5k | property_ranges[i * num_properties + p]; |
69 | 77.5k | } |
70 | 82.9k | } |
71 | 5.43k | } |
72 | 25.3k | return true; |
73 | 25.3k | } |
74 | | |
75 | | Status DecodeTree(BitReader *br, ANSSymbolReader *reader, |
76 | | const std::vector<uint8_t> &context_map, Tree *tree, |
77 | 25.5k | size_t tree_size_limit) { |
78 | 25.5k | size_t leaf_id = 0; |
79 | 25.5k | size_t to_decode = 1; |
80 | 25.5k | tree->clear(); |
81 | 4.37M | while (to_decode > 0) { |
82 | 4.35M | JXL_RETURN_IF_ERROR(br->AllReadsWithinBounds()); |
83 | 4.35M | if (tree->size() > tree_size_limit) { |
84 | 3 | return JXL_FAILURE("Tree is too large: %" PRIuS " nodes vs %" PRIuS |
85 | 3 | " max nodes", |
86 | 3 | tree->size(), tree_size_limit); |
87 | 3 | } |
88 | 4.35M | to_decode--; |
89 | 4.35M | uint32_t prop1 = reader->ReadHybridUint(kPropertyContext, br, context_map); |
90 | 4.35M | if (prop1 > 256) return JXL_FAILURE("Invalid tree property value"); |
91 | 4.35M | int property = prop1 - 1; |
92 | 4.35M | if (property == -1) { |
93 | 31.4k | size_t predictor = |
94 | 31.4k | reader->ReadHybridUint(kPredictorContext, br, context_map); |
95 | 31.4k | if (predictor >= kNumModularPredictors) { |
96 | 2 | return JXL_FAILURE("Invalid predictor"); |
97 | 2 | } |
98 | 31.4k | int64_t predictor_offset = |
99 | 31.4k | UnpackSigned(reader->ReadHybridUint(kOffsetContext, br, context_map)); |
100 | 31.4k | uint32_t mul_log = |
101 | 31.4k | reader->ReadHybridUint(kMultiplierLogContext, br, context_map); |
102 | 31.4k | if (mul_log >= 31) { |
103 | 2 | return JXL_FAILURE("Invalid multiplier logarithm"); |
104 | 2 | } |
105 | 31.4k | uint32_t mul_bits = |
106 | 31.4k | reader->ReadHybridUint(kMultiplierBitsContext, br, context_map); |
107 | 31.4k | if (mul_bits >= (1u << (31u - mul_log)) - 1u) { |
108 | 2 | return JXL_FAILURE("Invalid multiplier"); |
109 | 2 | } |
110 | 31.4k | uint32_t multiplier = (mul_bits + 1U) << mul_log; |
111 | 31.4k | tree->emplace_back(-1, 0, leaf_id++, 0, static_cast<Predictor>(predictor), |
112 | 31.4k | predictor_offset, multiplier); |
113 | 31.4k | continue; |
114 | 31.4k | } |
115 | 4.32M | int splitval = |
116 | 4.32M | UnpackSigned(reader->ReadHybridUint(kSplitValContext, br, context_map)); |
117 | 4.32M | tree->emplace_back(property, splitval, tree->size() + to_decode + 1, |
118 | 4.32M | tree->size() + to_decode + 2, Predictor::Zero, 0, 1); |
119 | 4.32M | to_decode += 2; |
120 | 4.32M | } |
121 | 25.3k | return ValidateTree(*tree); |
122 | 25.5k | } |
123 | | } // namespace |
124 | | |
125 | | Status DecodeTree(JxlMemoryManager *memory_manager, BitReader *br, Tree *tree, |
126 | 25.8k | size_t tree_size_limit) { |
127 | 25.8k | std::vector<uint8_t> tree_context_map; |
128 | 25.8k | ANSCode tree_code; |
129 | 25.8k | JXL_RETURN_IF_ERROR(DecodeHistograms(memory_manager, br, kNumTreeContexts, |
130 | 25.8k | &tree_code, &tree_context_map)); |
131 | | // TODO(eustas): investigate more infinite tree cases. |
132 | 25.5k | if (tree_code.degenerate_symbols[tree_context_map[kPropertyContext]] > 0) { |
133 | 3 | return JXL_FAILURE("Infinite tree"); |
134 | 3 | } |
135 | 51.1k | JXL_ASSIGN_OR_RETURN(ANSSymbolReader reader, |
136 | 51.1k | ANSSymbolReader::Create(&tree_code, br)); |
137 | 51.1k | JXL_RETURN_IF_ERROR(DecodeTree(br, &reader, tree_context_map, tree, |
138 | 51.1k | std::min(tree_size_limit, kMaxTreeSize))); |
139 | 25.3k | if (!reader.CheckANSFinalState()) { |
140 | 0 | return JXL_FAILURE("ANS decode final state failed"); |
141 | 0 | } |
142 | 25.3k | return true; |
143 | 25.3k | } |
144 | | |
145 | | } // namespace jxl |