Coverage Report

Created: 2025-07-23 08:18

/src/libjxl/lib/jxl/enc_fields.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/enc_fields.h"
7
8
#include <cinttypes>  // PRIu64
9
#include <cstddef>
10
#include <cstdint>
11
#include <cstring>
12
13
#include "lib/jxl/base/compiler_specific.h"
14
#include "lib/jxl/base/status.h"
15
#include "lib/jxl/enc_aux_out.h"
16
#include "lib/jxl/field_encodings.h"
17
#include "lib/jxl/fields.h"
18
#include "lib/jxl/frame_header.h"
19
#include "lib/jxl/headers.h"
20
#include "lib/jxl/image_metadata.h"
21
#include "lib/jxl/quantizer.h"
22
23
namespace jxl {
24
25
namespace {
26
using ::jxl::fields_internal::VisitorBase;
27
class WriteVisitor : public VisitorBase {
28
 public:
29
  WriteVisitor(const size_t extension_bits, BitWriter* JXL_RESTRICT writer)
30
32.6k
      : extension_bits_(extension_bits), writer_(writer) {}
31
32
  Status Bits(const size_t bits, const uint32_t /*default_value*/,
33
58.5k
              uint32_t* JXL_RESTRICT value) override {
34
58.5k
    ok_ &= BitsCoder::Write(bits, *value, writer_);
35
58.5k
    return true;
36
58.5k
  }
37
  Status U32(const U32Enc enc, const uint32_t /*default_value*/,
38
28.4k
             uint32_t* JXL_RESTRICT value) override {
39
28.4k
    ok_ &= U32Coder::Write(enc, *value, writer_);
40
28.4k
    return true;
41
28.4k
  }
42
43
  Status U64(const uint64_t /*default_value*/,
44
6.55k
             uint64_t* JXL_RESTRICT value) override {
45
6.55k
    ok_ &= U64Coder::Write(*value, writer_);
46
6.55k
    return true;
47
6.55k
  }
48
49
  Status F16(const float /*default_value*/,
50
0
             float* JXL_RESTRICT value) override {
51
0
    ok_ &= F16Coder::Write(*value, writer_);
52
0
    return true;
53
0
  }
54
55
4.06k
  Status BeginExtensions(uint64_t* JXL_RESTRICT extensions) override {
56
4.06k
    JXL_QUIET_RETURN_IF_ERROR(VisitorBase::BeginExtensions(extensions));
57
4.06k
    if (*extensions == 0) {
58
4.06k
      JXL_ENSURE(extension_bits_ == 0);
59
4.06k
      return true;
60
4.06k
    }
61
    // TODO(janwas): extend API to pass in array of extension_bits, one per
62
    // extension. We currently ascribe all bits to the first extension, but
63
    // this is only an encoder limitation. NOTE: extension_bits_ can be zero
64
    // if an extension does not require any additional fields.
65
0
    ok_ &= U64Coder::Write(extension_bits_, writer_);
66
    // For each nonzero bit except the lowest/first (already written):
67
0
    for (uint64_t remaining_extensions = *extensions & (*extensions - 1);
68
0
         remaining_extensions != 0;
69
0
         remaining_extensions &= remaining_extensions - 1) {
70
0
      ok_ &= U64Coder::Write(0, writer_);
71
0
    }
72
0
    return true;
73
4.06k
  }
74
  // EndExtensions = default.
75
76
32.6k
  Status OK() const { return ok_; }
77
78
 private:
79
  const size_t extension_bits_;
80
  BitWriter* JXL_RESTRICT writer_;
81
  bool ok_ = true;
82
};
83
}  // namespace
84
85
Status Bundle::Write(const Fields& fields, BitWriter* writer, LayerType layer,
86
32.6k
                     AuxOut* aux_out) {
87
32.6k
  size_t extension_bits;
88
32.6k
  size_t total_bits;
89
32.6k
  JXL_RETURN_IF_ERROR(Bundle::CanEncode(fields, &extension_bits, &total_bits));
90
91
32.6k
  return writer->WithMaxBits(total_bits, layer, aux_out, [&] {
92
32.6k
    WriteVisitor visitor(extension_bits, writer);
93
32.6k
    JXL_RETURN_IF_ERROR(visitor.VisitConst(fields));
94
32.6k
    return visitor.OK();
95
32.6k
  });
96
32.6k
}
97
98
// Returns false if the value is too large to encode.
99
Status BitsCoder::Write(const size_t bits, const uint32_t value,
100
58.5k
                        BitWriter* JXL_RESTRICT writer) {
101
58.5k
  if (value >= (1ULL << bits)) {
102
0
    return JXL_FAILURE("Value %d too large to encode in %" PRIu64 " bits",
103
0
                       value, static_cast<uint64_t>(bits));
104
0
  }
105
58.5k
  writer->Write(bits, value);
106
58.5k
  return true;
107
58.5k
}
108
109
// Returns false if the value is too large to encode.
110
Status U32Coder::Write(const U32Enc enc, const uint32_t value,
111
40.0k
                       BitWriter* JXL_RESTRICT writer) {
112
40.0k
  uint32_t selector;
113
40.0k
  size_t total_bits;
114
40.0k
  JXL_RETURN_IF_ERROR(ChooseSelector(enc, value, &selector, &total_bits));
115
116
40.0k
  writer->Write(2, selector);
117
118
40.0k
  const U32Distr d = enc.GetDistr(selector);
119
40.0k
  if (!d.IsDirect()) {  // Nothing more to write for direct encoding
120
19.5k
    const uint32_t offset = d.Offset();
121
19.5k
    JXL_ENSURE(value >= offset);
122
19.5k
    writer->Write(total_bits - 2, value - offset);
123
19.5k
  }
124
125
40.0k
  return true;
126
40.0k
}
127
128
// Returns false if the value is too large to encode.
129
6.55k
Status U64Coder::Write(uint64_t value, BitWriter* JXL_RESTRICT writer) {
130
6.55k
  if (value == 0) {
131
    // Selector: use 0 bits, value 0
132
5.54k
    writer->Write(2, 0);
133
5.54k
  } else if (value <= 16) {
134
    // Selector: use 4 bits, value 1..16
135
1.00k
    writer->Write(2, 1);
136
1.00k
    writer->Write(4, value - 1);
137
1.00k
  } else if (value <= 272) {
138
    // Selector: use 8 bits, value 17..272
139
0
    writer->Write(2, 2);
140
0
    writer->Write(8, value - 17);
141
0
  } else {
142
    // Selector: varint, first a 12-bit group, after that per 8-bit group.
143
0
    writer->Write(2, 3);
144
0
    writer->Write(12, value & 4095);
145
0
    value >>= 12;
146
0
    int shift = 12;
147
0
    while (value > 0 && shift < 60) {
148
      // Indicate varint not done
149
0
      writer->Write(1, 1);
150
0
      writer->Write(8, value & 255);
151
0
      value >>= 8;
152
0
      shift += 8;
153
0
    }
154
0
    if (value > 0) {
155
      // This only could happen if shift == N - 4.
156
0
      writer->Write(1, 1);
157
0
      writer->Write(4, value & 15);
158
      // Implicitly closed sequence, no extra stop bit is required.
159
0
    } else {
160
      // Indicate end of varint
161
0
      writer->Write(1, 0);
162
0
    }
163
0
  }
164
165
6.55k
  return true;
166
6.55k
}
167
168
6.00k
Status F16Coder::Write(float value, BitWriter* JXL_RESTRICT writer) {
169
6.00k
  uint32_t bits32;
170
6.00k
  memcpy(&bits32, &value, sizeof(bits32));
171
6.00k
  const uint32_t sign = bits32 >> 31;
172
6.00k
  const uint32_t biased_exp32 = (bits32 >> 23) & 0xFF;
173
6.00k
  const uint32_t mantissa32 = bits32 & 0x7FFFFF;
174
175
6.00k
  const int32_t exp = static_cast<int32_t>(biased_exp32) - 127;
176
6.00k
  if (JXL_UNLIKELY(exp > 15)) {
177
0
    return JXL_FAILURE("Too big to encode, CanEncode should return false");
178
0
  }
179
180
  // Tiny or zero => zero.
181
6.00k
  if (exp < -24) {
182
0
    writer->Write(16, 0);
183
0
    return true;
184
0
  }
185
186
6.00k
  uint32_t biased_exp16;
187
6.00k
  uint32_t mantissa16;
188
189
  // exp = [-24, -15] => subnormal
190
6.00k
  if (JXL_UNLIKELY(exp < -14)) {
191
0
    biased_exp16 = 0;
192
0
    const uint32_t sub_exp = static_cast<uint32_t>(-14 - exp);
193
0
    JXL_ENSURE(1 <= sub_exp && sub_exp < 11);
194
0
    mantissa16 = (1 << (10 - sub_exp)) + (mantissa32 >> (13 + sub_exp));
195
6.00k
  } else {
196
    // exp = [-14, 15]
197
6.00k
    biased_exp16 = static_cast<uint32_t>(exp + 15);
198
6.00k
    JXL_ENSURE(1 <= biased_exp16 && biased_exp16 < 31);
199
6.00k
    mantissa16 = mantissa32 >> 13;
200
6.00k
  }
201
202
6.00k
  JXL_ENSURE(mantissa16 < 1024);
203
6.00k
  const uint32_t bits16 = (sign << 15) | (biased_exp16 << 10) | mantissa16;
204
6.00k
  JXL_ENSURE(bits16 < 0x10000);
205
6.00k
  writer->Write(16, bits16);
206
6.00k
  return true;
207
6.00k
}
208
209
Status WriteCodestreamHeaders(CodecMetadata* metadata, BitWriter* writer,
210
2.13k
                              AuxOut* aux_out) {
211
  // Marker/signature
212
2.13k
  JXL_RETURN_IF_ERROR(writer->WithMaxBits(16, LayerType::Header, aux_out, [&] {
213
2.13k
    writer->Write(8, 0xFF);
214
2.13k
    writer->Write(8, kCodestreamMarker);
215
2.13k
    return true;
216
2.13k
  }));
217
218
2.13k
  JXL_RETURN_IF_ERROR(
219
2.13k
      WriteSizeHeader(metadata->size, writer, LayerType::Header, aux_out));
220
221
2.13k
  JXL_RETURN_IF_ERROR(
222
2.13k
      WriteImageMetadata(metadata->m, writer, LayerType::Header, aux_out));
223
224
2.13k
  metadata->transform_data.nonserialized_xyb_encoded = metadata->m.xyb_encoded;
225
2.13k
  JXL_RETURN_IF_ERROR(Bundle::Write(metadata->transform_data, writer,
226
2.13k
                                    LayerType::Header, aux_out));
227
228
2.13k
  return true;
229
2.13k
}
230
231
Status WriteFrameHeader(const FrameHeader& frame,
232
3.13k
                        BitWriter* JXL_RESTRICT writer, AuxOut* aux_out) {
233
3.13k
  return Bundle::Write(frame, writer, LayerType::Header, aux_out);
234
3.13k
}
235
236
Status WriteImageMetadata(const ImageMetadata& metadata,
237
                          BitWriter* JXL_RESTRICT writer, LayerType layer,
238
2.13k
                          AuxOut* aux_out) {
239
2.13k
  return Bundle::Write(metadata, writer, layer, aux_out);
240
2.13k
}
241
242
Status WriteQuantizerParams(const QuantizerParams& params,
243
                            BitWriter* JXL_RESTRICT writer, LayerType layer,
244
2.13k
                            AuxOut* aux_out) {
245
2.13k
  return Bundle::Write(params, writer, layer, aux_out);
246
2.13k
}
247
248
Status WriteSizeHeader(const SizeHeader& size, BitWriter* JXL_RESTRICT writer,
249
2.13k
                       LayerType layer, AuxOut* aux_out) {
250
2.13k
  return Bundle::Write(size, writer, layer, aux_out);
251
2.13k
}
252
253
}  // namespace jxl