/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 <array> |
10 | | #include <cstddef> |
11 | | #include <cstdint> |
12 | | #include <limits> |
13 | | #include <vector> |
14 | | |
15 | | #include "lib/jxl/base/compiler_specific.h" |
16 | | #include "lib/jxl/base/status.h" |
17 | | #include "lib/jxl/field_encodings.h" |
18 | | #include "lib/jxl/modular/encoding/context_predict.h" |
19 | | #include "lib/jxl/modular/encoding/dec_ma.h" |
20 | | #include "lib/jxl/modular/modular_image.h" |
21 | | #include "lib/jxl/modular/options.h" |
22 | | #include "lib/jxl/modular/transform/transform.h" |
23 | | |
24 | | namespace jxl { |
25 | | |
26 | | struct ANSCode; |
27 | | class BitReader; |
28 | | |
29 | | // Valid range of properties for using lookup tables instead of trees. |
30 | | constexpr int32_t kPropRangeFast = 512 << 4; |
31 | | |
32 | | struct GroupHeader : public Fields { |
33 | | GroupHeader(); |
34 | | |
35 | | JXL_FIELDS_NAME(GroupHeader) |
36 | | |
37 | 267k | Status VisitFields(Visitor *JXL_RESTRICT visitor) override { |
38 | 267k | JXL_QUIET_RETURN_IF_ERROR(visitor->Bool(false, &use_global_tree)); |
39 | 267k | JXL_QUIET_RETURN_IF_ERROR(visitor->VisitNested(&wp_header)); |
40 | 265k | uint32_t num_transforms = static_cast<uint32_t>(transforms.size()); |
41 | 265k | JXL_QUIET_RETURN_IF_ERROR(visitor->U32(Val(0), Val(1), BitsOffset(4, 2), |
42 | 265k | BitsOffset(8, 18), 0, |
43 | 265k | &num_transforms)); |
44 | 265k | if (visitor->IsReading()) transforms.resize(num_transforms); |
45 | 283k | for (size_t i = 0; i < num_transforms; i++) { |
46 | 20.4k | JXL_QUIET_RETURN_IF_ERROR(visitor->VisitNested(&transforms[i])); |
47 | 20.4k | } |
48 | 263k | return true; |
49 | 265k | } |
50 | | |
51 | | bool use_global_tree; |
52 | | weighted::Header wp_header; |
53 | | |
54 | | std::vector<Transform> transforms; |
55 | | }; |
56 | | |
57 | | FlatTree FilterTree(const Tree &global_tree, |
58 | | std::array<pixel_type, kNumStaticProperties> &static_props, |
59 | | size_t *num_props, bool *use_wp, bool *wp_only, |
60 | | bool *gradient_only); |
61 | | |
62 | | template <typename T, bool HAS_OFFSETS, bool HAS_MULTIPLIERS> |
63 | | struct TreeLut { |
64 | | std::array<T, 2 * kPropRangeFast> context_lookup; |
65 | | std::array<int8_t, HAS_OFFSETS ? (2 * kPropRangeFast) : 0> offsets; |
66 | | std::array<int8_t, HAS_MULTIPLIERS ? (2 * kPropRangeFast) : 0> multipliers; |
67 | | }; |
68 | | |
69 | | template <typename T, bool HAS_OFFSETS, bool HAS_MULTIPLIERS> |
70 | | bool TreeToLookupTable(const FlatTree &tree, |
71 | 13.2k | TreeLut<T, HAS_OFFSETS, HAS_MULTIPLIERS> &lut) { |
72 | 13.2k | struct TreeRange { |
73 | | // Begin *excluded*, end *included*. This works best with > vs <= decision |
74 | | // nodes. |
75 | 13.2k | int begin, end; |
76 | 13.2k | size_t pos; |
77 | 13.2k | }; |
78 | 13.2k | std::vector<TreeRange> ranges; |
79 | 13.2k | ranges.push_back(TreeRange{-kPropRangeFast - 1, kPropRangeFast - 1, 0}); |
80 | 106k | while (!ranges.empty()) { |
81 | 95.7k | TreeRange cur = ranges.back(); |
82 | 95.7k | ranges.pop_back(); |
83 | 95.7k | if (cur.begin < -kPropRangeFast - 1 || cur.begin >= kPropRangeFast - 1 || |
84 | 95.7k | cur.end > kPropRangeFast - 1) { |
85 | | // Tree is outside the allowed range, exit. |
86 | 0 | return false; |
87 | 0 | } |
88 | 95.7k | auto &node = tree[cur.pos]; |
89 | | // Leaf. |
90 | 95.7k | if (node.property0 == -1) { |
91 | 64.2k | if (node.predictor_offset < std::numeric_limits<int8_t>::min() || |
92 | 64.2k | node.predictor_offset > std::numeric_limits<int8_t>::max()) { |
93 | 162 | return false; |
94 | 162 | } |
95 | 64.1k | if (node.multiplier < std::numeric_limits<int8_t>::min() || |
96 | 64.1k | node.multiplier > std::numeric_limits<int8_t>::max()) { |
97 | 660 | return false; |
98 | 660 | } |
99 | 63.4k | if (!HAS_MULTIPLIERS && node.multiplier != 1) { |
100 | 1.01k | return false; |
101 | 1.01k | } |
102 | 62.4k | if (!HAS_OFFSETS && node.predictor_offset != 0) { |
103 | 182 | return false; |
104 | 182 | } |
105 | 183M | for (int i = cur.begin + 1; i < cur.end + 1; i++) { |
106 | 183M | lut.context_lookup[i + kPropRangeFast] = node.childID; |
107 | 183M | if (HAS_MULTIPLIERS) { |
108 | 0 | lut.multipliers[i + kPropRangeFast] = node.multiplier; |
109 | 0 | } |
110 | 183M | if (HAS_OFFSETS) { |
111 | 0 | lut.offsets[i + kPropRangeFast] = node.predictor_offset; |
112 | 0 | } |
113 | 183M | } |
114 | 62.2k | continue; |
115 | 62.4k | } |
116 | | // > side of top node. |
117 | 31.4k | if (node.properties[0] >= kNumStaticProperties) { |
118 | 8.51k | ranges.push_back(TreeRange({node.splitvals[0], cur.end, node.childID})); |
119 | 8.51k | ranges.push_back( |
120 | 8.51k | TreeRange({node.splitval0, node.splitvals[0], node.childID + 1})); |
121 | 22.9k | } else { |
122 | 22.9k | ranges.push_back(TreeRange({node.splitval0, cur.end, node.childID})); |
123 | 22.9k | } |
124 | | // <= side |
125 | 31.4k | if (node.properties[1] >= kNumStaticProperties) { |
126 | 11.1k | ranges.push_back( |
127 | 11.1k | TreeRange({node.splitvals[1], node.splitval0, node.childID + 2})); |
128 | 11.1k | ranges.push_back( |
129 | 11.1k | TreeRange({cur.begin, node.splitvals[1], node.childID + 3})); |
130 | 20.3k | } else { |
131 | 20.3k | ranges.push_back( |
132 | 20.3k | TreeRange({cur.begin, node.splitval0, node.childID + 2})); |
133 | 20.3k | } |
134 | 31.4k | } |
135 | 11.1k | return true; |
136 | 13.2k | } bool jxl::TreeToLookupTable<unsigned char, false, false>(std::__1::vector<jxl::FlatDecisionNode, std::__1::allocator<jxl::FlatDecisionNode> > const&, jxl::TreeLut<unsigned char, false, false>&) Line | Count | Source | 71 | 3.94k | TreeLut<T, HAS_OFFSETS, HAS_MULTIPLIERS> &lut) { | 72 | 3.94k | struct TreeRange { | 73 | | // Begin *excluded*, end *included*. This works best with > vs <= decision | 74 | | // nodes. | 75 | 3.94k | int begin, end; | 76 | 3.94k | size_t pos; | 77 | 3.94k | }; | 78 | 3.94k | std::vector<TreeRange> ranges; | 79 | 3.94k | ranges.push_back(TreeRange{-kPropRangeFast - 1, kPropRangeFast - 1, 0}); | 80 | 7.92k | while (!ranges.empty()) { | 81 | 6.00k | TreeRange cur = ranges.back(); | 82 | 6.00k | ranges.pop_back(); | 83 | 6.00k | if (cur.begin < -kPropRangeFast - 1 || cur.begin >= kPropRangeFast - 1 || | 84 | 6.00k | cur.end > kPropRangeFast - 1) { | 85 | | // Tree is outside the allowed range, exit. | 86 | 0 | return false; | 87 | 0 | } | 88 | 6.00k | auto &node = tree[cur.pos]; | 89 | | // Leaf. | 90 | 6.00k | if (node.property0 == -1) { | 91 | 5.24k | if (node.predictor_offset < std::numeric_limits<int8_t>::min() || | 92 | 5.24k | node.predictor_offset > std::numeric_limits<int8_t>::max()) { | 93 | 162 | return false; | 94 | 162 | } | 95 | 5.08k | if (node.multiplier < std::numeric_limits<int8_t>::min() || | 96 | 5.08k | node.multiplier > std::numeric_limits<int8_t>::max()) { | 97 | 660 | return false; | 98 | 660 | } | 99 | 4.42k | if (!HAS_MULTIPLIERS && node.multiplier != 1) { | 100 | 1.01k | return false; | 101 | 1.01k | } | 102 | 3.40k | if (!HAS_OFFSETS && node.predictor_offset != 0) { | 103 | 182 | return false; | 104 | 182 | } | 105 | 31.4M | for (int i = cur.begin + 1; i < cur.end + 1; i++) { | 106 | 31.4M | lut.context_lookup[i + kPropRangeFast] = node.childID; | 107 | 31.4M | if (HAS_MULTIPLIERS) { | 108 | 0 | lut.multipliers[i + kPropRangeFast] = node.multiplier; | 109 | 0 | } | 110 | 31.4M | if (HAS_OFFSETS) { | 111 | 0 | lut.offsets[i + kPropRangeFast] = node.predictor_offset; | 112 | 0 | } | 113 | 31.4M | } | 114 | 3.22k | continue; | 115 | 3.40k | } | 116 | | // > side of top node. | 117 | 760 | if (node.properties[0] >= kNumStaticProperties) { | 118 | 263 | ranges.push_back(TreeRange({node.splitvals[0], cur.end, node.childID})); | 119 | 263 | ranges.push_back( | 120 | 263 | TreeRange({node.splitval0, node.splitvals[0], node.childID + 1})); | 121 | 497 | } else { | 122 | 497 | ranges.push_back(TreeRange({node.splitval0, cur.end, node.childID})); | 123 | 497 | } | 124 | | // <= side | 125 | 760 | if (node.properties[1] >= kNumStaticProperties) { | 126 | 318 | ranges.push_back( | 127 | 318 | TreeRange({node.splitvals[1], node.splitval0, node.childID + 2})); | 128 | 318 | ranges.push_back( | 129 | 318 | TreeRange({cur.begin, node.splitvals[1], node.childID + 3})); | 130 | 442 | } else { | 131 | 442 | ranges.push_back( | 132 | 442 | TreeRange({cur.begin, node.splitval0, node.childID + 2})); | 133 | 442 | } | 134 | 760 | } | 135 | 1.92k | return true; | 136 | 3.94k | } |
bool jxl::TreeToLookupTable<unsigned short, false, false>(std::__1::vector<jxl::FlatDecisionNode, std::__1::allocator<jxl::FlatDecisionNode> > const&, jxl::TreeLut<unsigned short, false, false>&) Line | Count | Source | 71 | 9.27k | TreeLut<T, HAS_OFFSETS, HAS_MULTIPLIERS> &lut) { | 72 | 9.27k | struct TreeRange { | 73 | | // Begin *excluded*, end *included*. This works best with > vs <= decision | 74 | | // nodes. | 75 | 9.27k | int begin, end; | 76 | 9.27k | size_t pos; | 77 | 9.27k | }; | 78 | 9.27k | std::vector<TreeRange> ranges; | 79 | 9.27k | ranges.push_back(TreeRange{-kPropRangeFast - 1, kPropRangeFast - 1, 0}); | 80 | 99.0k | while (!ranges.empty()) { | 81 | 89.7k | TreeRange cur = ranges.back(); | 82 | 89.7k | ranges.pop_back(); | 83 | 89.7k | if (cur.begin < -kPropRangeFast - 1 || cur.begin >= kPropRangeFast - 1 || | 84 | 89.7k | cur.end > kPropRangeFast - 1) { | 85 | | // Tree is outside the allowed range, exit. | 86 | 0 | return false; | 87 | 0 | } | 88 | 89.7k | auto &node = tree[cur.pos]; | 89 | | // Leaf. | 90 | 89.7k | if (node.property0 == -1) { | 91 | 59.0k | if (node.predictor_offset < std::numeric_limits<int8_t>::min() || | 92 | 59.0k | node.predictor_offset > std::numeric_limits<int8_t>::max()) { | 93 | 0 | return false; | 94 | 0 | } | 95 | 59.0k | if (node.multiplier < std::numeric_limits<int8_t>::min() || | 96 | 59.0k | node.multiplier > std::numeric_limits<int8_t>::max()) { | 97 | 0 | return false; | 98 | 0 | } | 99 | 59.0k | if (!HAS_MULTIPLIERS && node.multiplier != 1) { | 100 | 0 | return false; | 101 | 0 | } | 102 | 59.0k | if (!HAS_OFFSETS && node.predictor_offset != 0) { | 103 | 0 | return false; | 104 | 0 | } | 105 | 152M | for (int i = cur.begin + 1; i < cur.end + 1; i++) { | 106 | 151M | lut.context_lookup[i + kPropRangeFast] = node.childID; | 107 | 151M | if (HAS_MULTIPLIERS) { | 108 | 0 | lut.multipliers[i + kPropRangeFast] = node.multiplier; | 109 | 0 | } | 110 | 151M | if (HAS_OFFSETS) { | 111 | 0 | lut.offsets[i + kPropRangeFast] = node.predictor_offset; | 112 | 0 | } | 113 | 151M | } | 114 | 59.0k | continue; | 115 | 59.0k | } | 116 | | // > side of top node. | 117 | 30.6k | if (node.properties[0] >= kNumStaticProperties) { | 118 | 8.24k | ranges.push_back(TreeRange({node.splitvals[0], cur.end, node.childID})); | 119 | 8.24k | ranges.push_back( | 120 | 8.24k | TreeRange({node.splitval0, node.splitvals[0], node.childID + 1})); | 121 | 22.4k | } else { | 122 | 22.4k | ranges.push_back(TreeRange({node.splitval0, cur.end, node.childID})); | 123 | 22.4k | } | 124 | | // <= side | 125 | 30.6k | if (node.properties[1] >= kNumStaticProperties) { | 126 | 10.8k | ranges.push_back( | 127 | 10.8k | TreeRange({node.splitvals[1], node.splitval0, node.childID + 2})); | 128 | 10.8k | ranges.push_back( | 129 | 10.8k | TreeRange({cur.begin, node.splitvals[1], node.childID + 3})); | 130 | 19.8k | } else { | 131 | 19.8k | ranges.push_back( | 132 | 19.8k | TreeRange({cur.begin, node.splitval0, node.childID + 2})); | 133 | 19.8k | } | 134 | 30.6k | } | 135 | 9.27k | return true; | 136 | 9.27k | } |
|
137 | | // TODO(veluca): make cleaner interfaces. |
138 | | |
139 | | Status ValidateChannelDimensions(const Image &image, |
140 | | const ModularOptions &options); |
141 | | |
142 | | Status ModularGenericDecompress(BitReader *br, Image &image, |
143 | | GroupHeader *header, size_t group_id, |
144 | | ModularOptions *options, |
145 | | bool undo_transforms = true, |
146 | | const Tree *tree = nullptr, |
147 | | const ANSCode *code = nullptr, |
148 | | const std::vector<uint8_t> *ctx_map = nullptr, |
149 | | bool allow_truncated_group = false); |
150 | | } // namespace jxl |
151 | | |
152 | | #endif // LIB_JXL_MODULAR_ENCODING_ENCODING_H_ |