/src/libjxl/lib/jxl/modular/encoding/encoding.h
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 | | #ifndef LIB_JXL_MODULAR_ENCODING_ENCODING_H_ |
7 | | #define LIB_JXL_MODULAR_ENCODING_ENCODING_H_ |
8 | | |
9 | | #include <stddef.h> |
10 | | #include <stdint.h> |
11 | | |
12 | | #include <vector> |
13 | | |
14 | | #include "lib/jxl/dec_ans.h" |
15 | | #include "lib/jxl/image.h" |
16 | | #include "lib/jxl/modular/encoding/context_predict.h" |
17 | | #include "lib/jxl/modular/encoding/dec_ma.h" |
18 | | #include "lib/jxl/modular/modular_image.h" |
19 | | #include "lib/jxl/modular/options.h" |
20 | | #include "lib/jxl/modular/transform/transform.h" |
21 | | |
22 | | namespace jxl { |
23 | | |
24 | | // Valid range of properties for using lookup tables instead of trees. |
25 | | constexpr int32_t kPropRangeFast = 512; |
26 | | |
27 | | struct GroupHeader : public Fields { |
28 | | GroupHeader(); |
29 | | |
30 | | JXL_FIELDS_NAME(GroupHeader) |
31 | | |
32 | 259k | Status VisitFields(Visitor *JXL_RESTRICT visitor) override { |
33 | 259k | JXL_QUIET_RETURN_IF_ERROR(visitor->Bool(false, &use_global_tree)); |
34 | 256k | JXL_QUIET_RETURN_IF_ERROR(visitor->VisitNested(&wp_header)); |
35 | 254k | uint32_t num_transforms = static_cast<uint32_t>(transforms.size()); |
36 | 254k | JXL_QUIET_RETURN_IF_ERROR(visitor->U32(Val(0), Val(1), BitsOffset(4, 2), |
37 | 254k | BitsOffset(8, 18), 0, |
38 | 254k | &num_transforms)); |
39 | 254k | if (visitor->IsReading()) transforms.resize(num_transforms); |
40 | 309k | for (size_t i = 0; i < num_transforms; i++) { |
41 | 59.9k | JXL_QUIET_RETURN_IF_ERROR(visitor->VisitNested(&transforms[i])); |
42 | 59.9k | } |
43 | 249k | return true; |
44 | 254k | } |
45 | | |
46 | | bool use_global_tree; |
47 | | weighted::Header wp_header; |
48 | | |
49 | | std::vector<Transform> transforms; |
50 | | }; |
51 | | |
52 | | FlatTree FilterTree(const Tree &global_tree, |
53 | | std::array<pixel_type, kNumStaticProperties> &static_props, |
54 | | size_t *num_props, bool *use_wp, bool *wp_only, |
55 | | bool *gradient_only); |
56 | | |
57 | | template <typename T> |
58 | | bool TreeToLookupTable(const FlatTree &tree, |
59 | | T context_lookup[2 * kPropRangeFast], |
60 | | int8_t offsets[2 * kPropRangeFast], |
61 | 35.8k | int8_t multipliers[2 * kPropRangeFast] = nullptr) { |
62 | 35.8k | struct TreeRange { |
63 | | // Begin *excluded*, end *included*. This works best with > vs <= decision |
64 | | // nodes. |
65 | 35.8k | int begin, end; |
66 | 35.8k | size_t pos; |
67 | 35.8k | }; |
68 | 35.8k | std::vector<TreeRange> ranges; |
69 | 35.8k | ranges.push_back(TreeRange{-kPropRangeFast - 1, kPropRangeFast - 1, 0}); |
70 | 126k | while (!ranges.empty()) { |
71 | 114k | TreeRange cur = ranges.back(); |
72 | 114k | ranges.pop_back(); |
73 | 114k | if (cur.begin < -kPropRangeFast - 1 || cur.begin >= kPropRangeFast - 1 || |
74 | 114k | cur.end > kPropRangeFast - 1) { |
75 | | // Tree is outside the allowed range, exit. |
76 | 0 | return false; |
77 | 0 | } |
78 | 114k | auto &node = tree[cur.pos]; |
79 | | // Leaf. |
80 | 114k | if (node.property0 == -1) { |
81 | 78.2k | if (node.predictor_offset < std::numeric_limits<int8_t>::min() || |
82 | 78.2k | node.predictor_offset > std::numeric_limits<int8_t>::max()) { |
83 | 0 | return false; |
84 | 0 | } |
85 | 78.2k | if (node.multiplier < std::numeric_limits<int8_t>::min() || |
86 | 78.2k | node.multiplier > std::numeric_limits<int8_t>::max()) { |
87 | 23.7k | return false; |
88 | 23.7k | } |
89 | 54.5k | if (multipliers == nullptr && node.multiplier != 1) { |
90 | 0 | return false; |
91 | 0 | } |
92 | 24.5M | for (int i = cur.begin + 1; i < cur.end + 1; i++) { |
93 | 24.4M | context_lookup[i + kPropRangeFast] = node.childID; |
94 | 24.4M | if (multipliers) multipliers[i + kPropRangeFast] = node.multiplier; |
95 | 24.4M | offsets[i + kPropRangeFast] = node.predictor_offset; |
96 | 24.4M | } |
97 | 54.5k | continue; |
98 | 54.5k | } |
99 | | // > side of top node. |
100 | 35.9k | if (node.properties[0] >= kNumStaticProperties) { |
101 | 28.5k | ranges.push_back(TreeRange({node.splitvals[0], cur.end, node.childID})); |
102 | 28.5k | ranges.push_back( |
103 | 28.5k | TreeRange({node.splitval0, node.splitvals[0], node.childID + 1})); |
104 | 28.5k | } else { |
105 | 7.44k | ranges.push_back(TreeRange({node.splitval0, cur.end, node.childID})); |
106 | 7.44k | } |
107 | | // <= side |
108 | 35.9k | if (node.properties[1] >= kNumStaticProperties) { |
109 | 1.74k | ranges.push_back( |
110 | 1.74k | TreeRange({node.splitvals[1], node.splitval0, node.childID + 2})); |
111 | 1.74k | ranges.push_back( |
112 | 1.74k | TreeRange({cur.begin, node.splitvals[1], node.childID + 3})); |
113 | 34.2k | } else { |
114 | 34.2k | ranges.push_back( |
115 | 34.2k | TreeRange({cur.begin, node.splitval0, node.childID + 2})); |
116 | 34.2k | } |
117 | 35.9k | } |
118 | 12.1k | return true; |
119 | 35.8k | } bool jxl::TreeToLookupTable<unsigned char>(std::__1::vector<jxl::FlatDecisionNode, std::__1::allocator<jxl::FlatDecisionNode> > const&, unsigned char*, signed char*, signed char*) Line | Count | Source | 61 | 35.5k | int8_t multipliers[2 * kPropRangeFast] = nullptr) { | 62 | 35.5k | struct TreeRange { | 63 | | // Begin *excluded*, end *included*. This works best with > vs <= decision | 64 | | // nodes. | 65 | 35.5k | int begin, end; | 66 | 35.5k | size_t pos; | 67 | 35.5k | }; | 68 | 35.5k | std::vector<TreeRange> ranges; | 69 | 35.5k | ranges.push_back(TreeRange{-kPropRangeFast - 1, kPropRangeFast - 1, 0}); | 70 | 125k | while (!ranges.empty()) { | 71 | 113k | TreeRange cur = ranges.back(); | 72 | 113k | ranges.pop_back(); | 73 | 113k | if (cur.begin < -kPropRangeFast - 1 || cur.begin >= kPropRangeFast - 1 || | 74 | 113k | cur.end > kPropRangeFast - 1) { | 75 | | // Tree is outside the allowed range, exit. | 76 | 0 | return false; | 77 | 0 | } | 78 | 113k | auto &node = tree[cur.pos]; | 79 | | // Leaf. | 80 | 113k | if (node.property0 == -1) { | 81 | 77.9k | if (node.predictor_offset < std::numeric_limits<int8_t>::min() || | 82 | 77.9k | node.predictor_offset > std::numeric_limits<int8_t>::max()) { | 83 | 0 | return false; | 84 | 0 | } | 85 | 77.9k | if (node.multiplier < std::numeric_limits<int8_t>::min() || | 86 | 77.9k | node.multiplier > std::numeric_limits<int8_t>::max()) { | 87 | 23.7k | return false; | 88 | 23.7k | } | 89 | 54.2k | if (multipliers == nullptr && node.multiplier != 1) { | 90 | 0 | return false; | 91 | 0 | } | 92 | 24.2M | for (int i = cur.begin + 1; i < cur.end + 1; i++) { | 93 | 24.1M | context_lookup[i + kPropRangeFast] = node.childID; | 94 | 24.1M | if (multipliers) multipliers[i + kPropRangeFast] = node.multiplier; | 95 | 24.1M | offsets[i + kPropRangeFast] = node.predictor_offset; | 96 | 24.1M | } | 97 | 54.2k | continue; | 98 | 54.2k | } | 99 | | // > side of top node. | 100 | 35.9k | if (node.properties[0] >= kNumStaticProperties) { | 101 | 28.5k | ranges.push_back(TreeRange({node.splitvals[0], cur.end, node.childID})); | 102 | 28.5k | ranges.push_back( | 103 | 28.5k | TreeRange({node.splitval0, node.splitvals[0], node.childID + 1})); | 104 | 28.5k | } else { | 105 | 7.44k | ranges.push_back(TreeRange({node.splitval0, cur.end, node.childID})); | 106 | 7.44k | } | 107 | | // <= side | 108 | 35.9k | if (node.properties[1] >= kNumStaticProperties) { | 109 | 1.74k | ranges.push_back( | 110 | 1.74k | TreeRange({node.splitvals[1], node.splitval0, node.childID + 2})); | 111 | 1.74k | ranges.push_back( | 112 | 1.74k | TreeRange({cur.begin, node.splitvals[1], node.childID + 3})); | 113 | 34.2k | } else { | 114 | 34.2k | ranges.push_back( | 115 | 34.2k | TreeRange({cur.begin, node.splitval0, node.childID + 2})); | 116 | 34.2k | } | 117 | 35.9k | } | 118 | 11.8k | return true; | 119 | 35.5k | } |
bool jxl::TreeToLookupTable<unsigned short>(std::__1::vector<jxl::FlatDecisionNode, std::__1::allocator<jxl::FlatDecisionNode> > const&, unsigned short*, signed char*, signed char*) Line | Count | Source | 61 | 276 | int8_t multipliers[2 * kPropRangeFast] = nullptr) { | 62 | 276 | struct TreeRange { | 63 | | // Begin *excluded*, end *included*. This works best with > vs <= decision | 64 | | // nodes. | 65 | 276 | int begin, end; | 66 | 276 | size_t pos; | 67 | 276 | }; | 68 | 276 | std::vector<TreeRange> ranges; | 69 | 276 | ranges.push_back(TreeRange{-kPropRangeFast - 1, kPropRangeFast - 1, 0}); | 70 | 552 | while (!ranges.empty()) { | 71 | 276 | TreeRange cur = ranges.back(); | 72 | 276 | ranges.pop_back(); | 73 | 276 | if (cur.begin < -kPropRangeFast - 1 || cur.begin >= kPropRangeFast - 1 || | 74 | 276 | cur.end > kPropRangeFast - 1) { | 75 | | // Tree is outside the allowed range, exit. | 76 | 0 | return false; | 77 | 0 | } | 78 | 276 | auto &node = tree[cur.pos]; | 79 | | // Leaf. | 80 | 276 | if (node.property0 == -1) { | 81 | 276 | if (node.predictor_offset < std::numeric_limits<int8_t>::min() || | 82 | 276 | node.predictor_offset > std::numeric_limits<int8_t>::max()) { | 83 | 0 | return false; | 84 | 0 | } | 85 | 276 | if (node.multiplier < std::numeric_limits<int8_t>::min() || | 86 | 276 | node.multiplier > std::numeric_limits<int8_t>::max()) { | 87 | 0 | return false; | 88 | 0 | } | 89 | 276 | if (multipliers == nullptr && node.multiplier != 1) { | 90 | 0 | return false; | 91 | 0 | } | 92 | 282k | for (int i = cur.begin + 1; i < cur.end + 1; i++) { | 93 | 282k | context_lookup[i + kPropRangeFast] = node.childID; | 94 | 282k | if (multipliers) multipliers[i + kPropRangeFast] = node.multiplier; | 95 | 282k | offsets[i + kPropRangeFast] = node.predictor_offset; | 96 | 282k | } | 97 | 276 | continue; | 98 | 276 | } | 99 | | // > side of top node. | 100 | 0 | if (node.properties[0] >= kNumStaticProperties) { | 101 | 0 | ranges.push_back(TreeRange({node.splitvals[0], cur.end, node.childID})); | 102 | 0 | ranges.push_back( | 103 | 0 | TreeRange({node.splitval0, node.splitvals[0], node.childID + 1})); | 104 | 0 | } else { | 105 | 0 | ranges.push_back(TreeRange({node.splitval0, cur.end, node.childID})); | 106 | 0 | } | 107 | | // <= side | 108 | 0 | if (node.properties[1] >= kNumStaticProperties) { | 109 | 0 | ranges.push_back( | 110 | 0 | TreeRange({node.splitvals[1], node.splitval0, node.childID + 2})); | 111 | 0 | ranges.push_back( | 112 | 0 | TreeRange({cur.begin, node.splitvals[1], node.childID + 3})); | 113 | 0 | } else { | 114 | 0 | ranges.push_back( | 115 | 0 | TreeRange({cur.begin, node.splitval0, node.childID + 2})); | 116 | 0 | } | 117 | 0 | } | 118 | 276 | return true; | 119 | 276 | } |
|
120 | | // TODO(veluca): make cleaner interfaces. |
121 | | |
122 | | Status ValidateChannelDimensions(const Image &image, |
123 | | const ModularOptions &options); |
124 | | |
125 | | Status ModularGenericDecompress(BitReader *br, Image &image, |
126 | | GroupHeader *header, size_t group_id, |
127 | | ModularOptions *options, |
128 | | bool undo_transforms = true, |
129 | | const Tree *tree = nullptr, |
130 | | const ANSCode *code = nullptr, |
131 | | const std::vector<uint8_t> *ctx_map = nullptr, |
132 | | bool allow_truncated_group = false); |
133 | | } // namespace jxl |
134 | | |
135 | | #endif // LIB_JXL_MODULAR_ENCODING_ENCODING_H_ |