Coverage Report

Created: 2026-09-14 07:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libjxl/lib/jxl/modular/encoding/encoding.h
Line
Count
Source
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
1.18M
  Status VisitFields(Visitor *JXL_RESTRICT visitor) override {
38
1.18M
    JXL_QUIET_RETURN_IF_ERROR(visitor->Bool(false, &use_global_tree));
39
1.17M
    JXL_QUIET_RETURN_IF_ERROR(visitor->VisitNested(&wp_header));
40
1.17M
    uint32_t num_transforms = static_cast<uint32_t>(transforms.size());
41
1.17M
    JXL_QUIET_RETURN_IF_ERROR(visitor->U32(Val(0), Val(1), BitsOffset(4, 2),
42
1.17M
                                           BitsOffset(8, 18), 0,
43
1.17M
                                           &num_transforms));
44
1.17M
    if (visitor->IsReading()) transforms.resize(num_transforms);
45
1.27M
    for (size_t i = 0; i < num_transforms; i++) {
46
110k
      JXL_QUIET_RETURN_IF_ERROR(visitor->VisitNested(&transforms[i]));
47
110k
    }
48
1.16M
    return true;
49
1.17M
  }
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
52.2k
                       TreeLut<T, HAS_OFFSETS, HAS_MULTIPLIERS> &lut) {
72
52.2k
  struct TreeRange {
73
    // Begin *excluded*, end *included*. This works best with > vs <= decision
74
    // nodes.
75
52.2k
    int begin, end;
76
52.2k
    size_t pos;
77
52.2k
  };
78
52.2k
  std::vector<TreeRange> ranges;
79
52.2k
  ranges.push_back(TreeRange{-kPropRangeFast - 1, kPropRangeFast - 1, 0});
80
282k
  while (!ranges.empty()) {
81
235k
    TreeRange cur = ranges.back();
82
235k
    ranges.pop_back();
83
235k
    if (cur.begin < -kPropRangeFast - 1 || cur.begin >= kPropRangeFast - 1 ||
84
235k
        cur.end > kPropRangeFast - 1) {
85
      // Tree is outside the allowed range, exit.
86
0
      return false;
87
0
    }
88
235k
    auto &node = tree[cur.pos];
89
    // Leaf.
90
235k
    if (node.property0 == -1) {
91
166k
      if (node.predictor_offset < std::numeric_limits<int8_t>::min() ||
92
166k
          node.predictor_offset > std::numeric_limits<int8_t>::max()) {
93
297
        return false;
94
297
      }
95
165k
      if (node.multiplier < std::numeric_limits<int8_t>::min() ||
96
165k
          node.multiplier > std::numeric_limits<int8_t>::max()) {
97
1.09k
        return false;
98
1.09k
      }
99
164k
      if (!HAS_MULTIPLIERS && node.multiplier != 1) {
100
2.89k
        return false;
101
2.89k
      }
102
161k
      if (!HAS_OFFSETS && node.predictor_offset != 0) {
103
714
        return false;
104
714
      }
105
774M
      for (int i = cur.begin + 1; i < cur.end + 1; i++) {
106
774M
        lut.context_lookup[i + kPropRangeFast] = node.childID;
107
774M
        if (HAS_MULTIPLIERS) {
108
0
          lut.multipliers[i + kPropRangeFast] = node.multiplier;
109
0
        }
110
774M
        if (HAS_OFFSETS) {
111
0
          lut.offsets[i + kPropRangeFast] = node.predictor_offset;
112
0
        }
113
774M
      }
114
161k
      continue;
115
161k
    }
116
    // > side of top node.
117
69.5k
    if (node.properties[0] >= kNumStaticProperties) {
118
20.2k
      ranges.push_back(TreeRange({node.splitvals[0], cur.end, node.childID}));
119
20.2k
      ranges.push_back(
120
20.2k
          TreeRange({node.splitval0, node.splitvals[0], node.childID + 1}));
121
49.2k
    } else {
122
49.2k
      ranges.push_back(TreeRange({node.splitval0, cur.end, node.childID}));
123
49.2k
    }
124
    // <= side
125
69.5k
    if (node.properties[1] >= kNumStaticProperties) {
126
24.2k
      ranges.push_back(
127
24.2k
          TreeRange({node.splitvals[1], node.splitval0, node.childID + 2}));
128
24.2k
      ranges.push_back(
129
24.2k
          TreeRange({cur.begin, node.splitvals[1], node.childID + 3}));
130
45.3k
    } else {
131
45.3k
      ranges.push_back(
132
45.3k
          TreeRange({cur.begin, node.splitval0, node.childID + 2}));
133
45.3k
    }
134
69.5k
  }
135
47.2k
  return true;
136
52.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
25.1k
                       TreeLut<T, HAS_OFFSETS, HAS_MULTIPLIERS> &lut) {
72
25.1k
  struct TreeRange {
73
    // Begin *excluded*, end *included*. This works best with > vs <= decision
74
    // nodes.
75
25.1k
    int begin, end;
76
25.1k
    size_t pos;
77
25.1k
  };
78
25.1k
  std::vector<TreeRange> ranges;
79
25.1k
  ranges.push_back(TreeRange{-kPropRangeFast - 1, kPropRangeFast - 1, 0});
80
93.7k
  while (!ranges.empty()) {
81
73.5k
    TreeRange cur = ranges.back();
82
73.5k
    ranges.pop_back();
83
73.5k
    if (cur.begin < -kPropRangeFast - 1 || cur.begin >= kPropRangeFast - 1 ||
84
73.5k
        cur.end > kPropRangeFast - 1) {
85
      // Tree is outside the allowed range, exit.
86
0
      return false;
87
0
    }
88
73.5k
    auto &node = tree[cur.pos];
89
    // Leaf.
90
73.5k
    if (node.property0 == -1) {
91
54.3k
      if (node.predictor_offset < std::numeric_limits<int8_t>::min() ||
92
54.2k
          node.predictor_offset > std::numeric_limits<int8_t>::max()) {
93
297
        return false;
94
297
      }
95
54.0k
      if (node.multiplier < std::numeric_limits<int8_t>::min() ||
96
54.0k
          node.multiplier > std::numeric_limits<int8_t>::max()) {
97
1.09k
        return false;
98
1.09k
      }
99
52.9k
      if (!HAS_MULTIPLIERS && node.multiplier != 1) {
100
2.89k
        return false;
101
2.89k
      }
102
50.0k
      if (!HAS_OFFSETS && node.predictor_offset != 0) {
103
714
        return false;
104
714
      }
105
330M
      for (int i = cur.begin + 1; i < cur.end + 1; i++) {
106
330M
        lut.context_lookup[i + kPropRangeFast] = node.childID;
107
330M
        if (HAS_MULTIPLIERS) {
108
0
          lut.multipliers[i + kPropRangeFast] = node.multiplier;
109
0
        }
110
330M
        if (HAS_OFFSETS) {
111
0
          lut.offsets[i + kPropRangeFast] = node.predictor_offset;
112
0
        }
113
330M
      }
114
49.3k
      continue;
115
50.0k
    }
116
    // > side of top node.
117
19.2k
    if (node.properties[0] >= kNumStaticProperties) {
118
4.80k
      ranges.push_back(TreeRange({node.splitvals[0], cur.end, node.childID}));
119
4.80k
      ranges.push_back(
120
4.80k
          TreeRange({node.splitval0, node.splitvals[0], node.childID + 1}));
121
14.4k
    } else {
122
14.4k
      ranges.push_back(TreeRange({node.splitval0, cur.end, node.childID}));
123
14.4k
    }
124
    // <= side
125
19.2k
    if (node.properties[1] >= kNumStaticProperties) {
126
5.16k
      ranges.push_back(
127
5.16k
          TreeRange({node.splitvals[1], node.splitval0, node.childID + 2}));
128
5.16k
      ranges.push_back(
129
5.16k
          TreeRange({cur.begin, node.splitvals[1], node.childID + 3}));
130
14.1k
    } else {
131
14.1k
      ranges.push_back(
132
14.1k
          TreeRange({cur.begin, node.splitval0, node.childID + 2}));
133
14.1k
    }
134
19.2k
  }
135
20.1k
  return true;
136
25.1k
}
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
27.0k
                       TreeLut<T, HAS_OFFSETS, HAS_MULTIPLIERS> &lut) {
72
27.0k
  struct TreeRange {
73
    // Begin *excluded*, end *included*. This works best with > vs <= decision
74
    // nodes.
75
27.0k
    int begin, end;
76
27.0k
    size_t pos;
77
27.0k
  };
78
27.0k
  std::vector<TreeRange> ranges;
79
27.0k
  ranges.push_back(TreeRange{-kPropRangeFast - 1, kPropRangeFast - 1, 0});
80
189k
  while (!ranges.empty()) {
81
162k
    TreeRange cur = ranges.back();
82
162k
    ranges.pop_back();
83
162k
    if (cur.begin < -kPropRangeFast - 1 || cur.begin >= kPropRangeFast - 1 ||
84
162k
        cur.end > kPropRangeFast - 1) {
85
      // Tree is outside the allowed range, exit.
86
0
      return false;
87
0
    }
88
162k
    auto &node = tree[cur.pos];
89
    // Leaf.
90
162k
    if (node.property0 == -1) {
91
111k
      if (node.predictor_offset < std::numeric_limits<int8_t>::min() ||
92
111k
          node.predictor_offset > std::numeric_limits<int8_t>::max()) {
93
0
        return false;
94
0
      }
95
111k
      if (node.multiplier < std::numeric_limits<int8_t>::min() ||
96
111k
          node.multiplier > std::numeric_limits<int8_t>::max()) {
97
0
        return false;
98
0
      }
99
111k
      if (!HAS_MULTIPLIERS && node.multiplier != 1) {
100
0
        return false;
101
0
      }
102
111k
      if (!HAS_OFFSETS && node.predictor_offset != 0) {
103
0
        return false;
104
0
      }
105
443M
      for (int i = cur.begin + 1; i < cur.end + 1; i++) {
106
443M
        lut.context_lookup[i + kPropRangeFast] = node.childID;
107
443M
        if (HAS_MULTIPLIERS) {
108
0
          lut.multipliers[i + kPropRangeFast] = node.multiplier;
109
0
        }
110
443M
        if (HAS_OFFSETS) {
111
0
          lut.offsets[i + kPropRangeFast] = node.predictor_offset;
112
0
        }
113
443M
      }
114
111k
      continue;
115
111k
    }
116
    // > side of top node.
117
50.2k
    if (node.properties[0] >= kNumStaticProperties) {
118
15.4k
      ranges.push_back(TreeRange({node.splitvals[0], cur.end, node.childID}));
119
15.4k
      ranges.push_back(
120
15.4k
          TreeRange({node.splitval0, node.splitvals[0], node.childID + 1}));
121
34.7k
    } else {
122
34.7k
      ranges.push_back(TreeRange({node.splitval0, cur.end, node.childID}));
123
34.7k
    }
124
    // <= side
125
50.2k
    if (node.properties[1] >= kNumStaticProperties) {
126
19.0k
      ranges.push_back(
127
19.0k
          TreeRange({node.splitvals[1], node.splitval0, node.childID + 2}));
128
19.0k
      ranges.push_back(
129
19.0k
          TreeRange({cur.begin, node.splitvals[1], node.childID + 3}));
130
31.1k
    } else {
131
31.1k
      ranges.push_back(
132
31.1k
          TreeRange({cur.begin, node.splitval0, node.childID + 2}));
133
31.1k
    }
134
50.2k
  }
135
27.0k
  return true;
136
27.0k
}
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_