/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 | 115k | Status VisitFields(Visitor *JXL_RESTRICT visitor) override { |
38 | 115k | JXL_QUIET_RETURN_IF_ERROR(visitor->Bool(false, &use_global_tree)); |
39 | 115k | JXL_QUIET_RETURN_IF_ERROR(visitor->VisitNested(&wp_header)); |
40 | 114k | uint32_t num_transforms = static_cast<uint32_t>(transforms.size()); |
41 | 114k | JXL_QUIET_RETURN_IF_ERROR(visitor->U32(Val(0), Val(1), BitsOffset(4, 2), |
42 | 114k | BitsOffset(8, 18), 0, |
43 | 114k | &num_transforms)); |
44 | 114k | if (visitor->IsReading()) transforms.resize(num_transforms); |
45 | 130k | for (size_t i = 0; i < num_transforms; i++) { |
46 | 16.4k | JXL_QUIET_RETURN_IF_ERROR(visitor->VisitNested(&transforms[i])); |
47 | 16.4k | } |
48 | 114k | return true; |
49 | 114k | } |
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 | 5.95k | TreeLut<T, HAS_OFFSETS, HAS_MULTIPLIERS> &lut) { |
72 | 5.95k | struct TreeRange { |
73 | | // Begin *excluded*, end *included*. This works best with > vs <= decision |
74 | | // nodes. |
75 | 5.95k | int begin, end; |
76 | 5.95k | size_t pos; |
77 | 5.95k | }; |
78 | 5.95k | std::vector<TreeRange> ranges; |
79 | 5.95k | ranges.push_back(TreeRange{-kPropRangeFast - 1, kPropRangeFast - 1, 0}); |
80 | 20.0k | while (!ranges.empty()) { |
81 | 17.8k | TreeRange cur = ranges.back(); |
82 | 17.8k | ranges.pop_back(); |
83 | 17.8k | if (cur.begin < -kPropRangeFast - 1 || cur.begin >= kPropRangeFast - 1 || |
84 | 17.8k | cur.end > kPropRangeFast - 1) { |
85 | | // Tree is outside the allowed range, exit. |
86 | 0 | return false; |
87 | 0 | } |
88 | 17.8k | auto &node = tree[cur.pos]; |
89 | | // Leaf. |
90 | 17.8k | if (node.property0 == -1) { |
91 | 13.2k | if (node.predictor_offset < std::numeric_limits<int8_t>::min() || |
92 | 13.2k | node.predictor_offset > std::numeric_limits<int8_t>::max()) { |
93 | 131 | return false; |
94 | 131 | } |
95 | 13.1k | if (node.multiplier < std::numeric_limits<int8_t>::min() || |
96 | 13.1k | node.multiplier > std::numeric_limits<int8_t>::max()) { |
97 | 467 | return false; |
98 | 467 | } |
99 | 12.6k | if (!HAS_MULTIPLIERS && node.multiplier != 1) { |
100 | 2.97k | return false; |
101 | 2.97k | } |
102 | 9.71k | if (!HAS_OFFSETS && node.predictor_offset != 0) { |
103 | 182 | return false; |
104 | 182 | } |
105 | 36.0M | for (int i = cur.begin + 1; i < cur.end + 1; i++) { |
106 | 36.0M | lut.context_lookup[i + kPropRangeFast] = node.childID; |
107 | 36.0M | if (HAS_MULTIPLIERS) { |
108 | 0 | lut.multipliers[i + kPropRangeFast] = node.multiplier; |
109 | 0 | } |
110 | 36.0M | if (HAS_OFFSETS) { |
111 | 0 | lut.offsets[i + kPropRangeFast] = node.predictor_offset; |
112 | 0 | } |
113 | 36.0M | } |
114 | 9.53k | continue; |
115 | 9.71k | } |
116 | | // > side of top node. |
117 | 4.50k | if (node.properties[0] >= kNumStaticProperties) { |
118 | 1.24k | ranges.push_back(TreeRange({node.splitvals[0], cur.end, node.childID})); |
119 | 1.24k | ranges.push_back( |
120 | 1.24k | TreeRange({node.splitval0, node.splitvals[0], node.childID + 1})); |
121 | 3.26k | } else { |
122 | 3.26k | ranges.push_back(TreeRange({node.splitval0, cur.end, node.childID})); |
123 | 3.26k | } |
124 | | // <= side |
125 | 4.50k | if (node.properties[1] >= kNumStaticProperties) { |
126 | 1.59k | ranges.push_back( |
127 | 1.59k | TreeRange({node.splitvals[1], node.splitval0, node.childID + 2})); |
128 | 1.59k | ranges.push_back( |
129 | 1.59k | TreeRange({cur.begin, node.splitvals[1], node.childID + 3})); |
130 | 2.91k | } else { |
131 | 2.91k | ranges.push_back( |
132 | 2.91k | TreeRange({cur.begin, node.splitval0, node.childID + 2})); |
133 | 2.91k | } |
134 | 4.50k | } |
135 | 2.19k | return true; |
136 | 5.95k | } 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 | 926 | TreeLut<T, HAS_OFFSETS, HAS_MULTIPLIERS> &lut) { | 72 | 926 | struct TreeRange { | 73 | | // Begin *excluded*, end *included*. This works best with > vs <= decision | 74 | | // nodes. | 75 | 926 | int begin, end; | 76 | 926 | size_t pos; | 77 | 926 | }; | 78 | 926 | std::vector<TreeRange> ranges; | 79 | 926 | ranges.push_back(TreeRange{-kPropRangeFast - 1, kPropRangeFast - 1, 0}); | 80 | 12.8k | while (!ranges.empty()) { | 81 | 11.8k | TreeRange cur = ranges.back(); | 82 | 11.8k | ranges.pop_back(); | 83 | 11.8k | if (cur.begin < -kPropRangeFast - 1 || cur.begin >= kPropRangeFast - 1 || | 84 | 11.8k | cur.end > kPropRangeFast - 1) { | 85 | | // Tree is outside the allowed range, exit. | 86 | 0 | return false; | 87 | 0 | } | 88 | 11.8k | auto &node = tree[cur.pos]; | 89 | | // Leaf. | 90 | 11.8k | if (node.property0 == -1) { | 91 | 7.76k | if (node.predictor_offset < std::numeric_limits<int8_t>::min() || | 92 | 7.76k | node.predictor_offset > std::numeric_limits<int8_t>::max()) { | 93 | 0 | return false; | 94 | 0 | } | 95 | 7.76k | if (node.multiplier < std::numeric_limits<int8_t>::min() || | 96 | 7.76k | node.multiplier > std::numeric_limits<int8_t>::max()) { | 97 | 0 | return false; | 98 | 0 | } | 99 | 7.76k | if (!HAS_MULTIPLIERS && node.multiplier != 1) { | 100 | 0 | return false; | 101 | 0 | } | 102 | 7.76k | if (!HAS_OFFSETS && node.predictor_offset != 0) { | 103 | 0 | return false; | 104 | 0 | } | 105 | 15.1M | for (int i = cur.begin + 1; i < cur.end + 1; i++) { | 106 | 15.1M | lut.context_lookup[i + kPropRangeFast] = node.childID; | 107 | 15.1M | if (HAS_MULTIPLIERS) { | 108 | 0 | lut.multipliers[i + kPropRangeFast] = node.multiplier; | 109 | 0 | } | 110 | 15.1M | if (HAS_OFFSETS) { | 111 | 0 | lut.offsets[i + kPropRangeFast] = node.predictor_offset; | 112 | 0 | } | 113 | 15.1M | } | 114 | 7.76k | continue; | 115 | 7.76k | } | 116 | | // > side of top node. | 117 | 4.12k | if (node.properties[0] >= kNumStaticProperties) { | 118 | 1.17k | ranges.push_back(TreeRange({node.splitvals[0], cur.end, node.childID})); | 119 | 1.17k | ranges.push_back( | 120 | 1.17k | TreeRange({node.splitval0, node.splitvals[0], node.childID + 1})); | 121 | 2.95k | } else { | 122 | 2.95k | ranges.push_back(TreeRange({node.splitval0, cur.end, node.childID})); | 123 | 2.95k | } | 124 | | // <= side | 125 | 4.12k | if (node.properties[1] >= kNumStaticProperties) { | 126 | 1.53k | ranges.push_back( | 127 | 1.53k | TreeRange({node.splitvals[1], node.splitval0, node.childID + 2})); | 128 | 1.53k | ranges.push_back( | 129 | 1.53k | TreeRange({cur.begin, node.splitvals[1], node.childID + 3})); | 130 | 2.59k | } else { | 131 | 2.59k | ranges.push_back( | 132 | 2.59k | TreeRange({cur.begin, node.splitval0, node.childID + 2})); | 133 | 2.59k | } | 134 | 4.12k | } | 135 | 926 | return true; | 136 | 926 | } |
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 | 5.03k | TreeLut<T, HAS_OFFSETS, HAS_MULTIPLIERS> &lut) { | 72 | 5.03k | struct TreeRange { | 73 | | // Begin *excluded*, end *included*. This works best with > vs <= decision | 74 | | // nodes. | 75 | 5.03k | int begin, end; | 76 | 5.03k | size_t pos; | 77 | 5.03k | }; | 78 | 5.03k | std::vector<TreeRange> ranges; | 79 | 5.03k | ranges.push_back(TreeRange{-kPropRangeFast - 1, kPropRangeFast - 1, 0}); | 80 | 7.18k | while (!ranges.empty()) { | 81 | 5.90k | TreeRange cur = ranges.back(); | 82 | 5.90k | ranges.pop_back(); | 83 | 5.90k | if (cur.begin < -kPropRangeFast - 1 || cur.begin >= kPropRangeFast - 1 || | 84 | 5.90k | cur.end > kPropRangeFast - 1) { | 85 | | // Tree is outside the allowed range, exit. | 86 | 0 | return false; | 87 | 0 | } | 88 | 5.90k | auto &node = tree[cur.pos]; | 89 | | // Leaf. | 90 | 5.90k | if (node.property0 == -1) { | 91 | 5.52k | if (node.predictor_offset < std::numeric_limits<int8_t>::min() || | 92 | 5.52k | node.predictor_offset > std::numeric_limits<int8_t>::max()) { | 93 | 131 | return false; | 94 | 131 | } | 95 | 5.39k | if (node.multiplier < std::numeric_limits<int8_t>::min() || | 96 | 5.39k | node.multiplier > std::numeric_limits<int8_t>::max()) { | 97 | 467 | return false; | 98 | 467 | } | 99 | 4.93k | if (!HAS_MULTIPLIERS && node.multiplier != 1) { | 100 | 2.97k | return false; | 101 | 2.97k | } | 102 | 1.95k | if (!HAS_OFFSETS && node.predictor_offset != 0) { | 103 | 182 | return false; | 104 | 182 | } | 105 | 20.9M | for (int i = cur.begin + 1; i < cur.end + 1; i++) { | 106 | 20.9M | lut.context_lookup[i + kPropRangeFast] = node.childID; | 107 | 20.9M | if (HAS_MULTIPLIERS) { | 108 | 0 | lut.multipliers[i + kPropRangeFast] = node.multiplier; | 109 | 0 | } | 110 | 20.9M | if (HAS_OFFSETS) { | 111 | 0 | lut.offsets[i + kPropRangeFast] = node.predictor_offset; | 112 | 0 | } | 113 | 20.9M | } | 114 | 1.77k | continue; | 115 | 1.95k | } | 116 | | // > side of top node. | 117 | 378 | if (node.properties[0] >= kNumStaticProperties) { | 118 | 63 | ranges.push_back(TreeRange({node.splitvals[0], cur.end, node.childID})); | 119 | 63 | ranges.push_back( | 120 | 63 | TreeRange({node.splitval0, node.splitvals[0], node.childID + 1})); | 121 | 315 | } else { | 122 | 315 | ranges.push_back(TreeRange({node.splitval0, cur.end, node.childID})); | 123 | 315 | } | 124 | | // <= side | 125 | 378 | if (node.properties[1] >= kNumStaticProperties) { | 126 | 61 | ranges.push_back( | 127 | 61 | TreeRange({node.splitvals[1], node.splitval0, node.childID + 2})); | 128 | 61 | ranges.push_back( | 129 | 61 | TreeRange({cur.begin, node.splitvals[1], node.childID + 3})); | 130 | 317 | } else { | 131 | 317 | ranges.push_back( | 132 | 317 | TreeRange({cur.begin, node.splitval0, node.childID + 2})); | 133 | 317 | } | 134 | 378 | } | 135 | 1.27k | return true; | 136 | 5.03k | } |
|
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_ |