/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 | 3.10k | : extension_bits_(extension_bits), writer_(writer) {} |
31 | | |
32 | | Status Bits(const size_t bits, const uint32_t /*default_value*/, |
33 | 6.97k | uint32_t* JXL_RESTRICT value) override { |
34 | 6.97k | ok_ &= BitsCoder::Write(bits, *value, writer_); |
35 | 6.97k | return true; |
36 | 6.97k | } |
37 | | Status U32(const U32Enc enc, const uint32_t /*default_value*/, |
38 | 3.28k | uint32_t* JXL_RESTRICT value) override { |
39 | 3.28k | ok_ &= U32Coder::Write(enc, *value, writer_); |
40 | 3.28k | return true; |
41 | 3.28k | } |
42 | | |
43 | | Status U64(const uint64_t /*default_value*/, |
44 | 906 | uint64_t* JXL_RESTRICT value) override { |
45 | 906 | ok_ &= U64Coder::Write(*value, writer_); |
46 | 906 | return true; |
47 | 906 | } |
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 | 623 | Status BeginExtensions(uint64_t* JXL_RESTRICT extensions) override { |
56 | 623 | JXL_QUIET_RETURN_IF_ERROR(VisitorBase::BeginExtensions(extensions)); |
57 | 623 | if (*extensions == 0) { |
58 | 623 | JXL_ENSURE(extension_bits_ == 0); |
59 | 623 | return true; |
60 | 623 | } |
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 | 623 | } |
74 | | // EndExtensions = default. |
75 | | |
76 | 3.10k | 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 | 3.10k | AuxOut* aux_out) { |
87 | 3.10k | size_t extension_bits; |
88 | 3.10k | size_t total_bits; |
89 | 3.10k | JXL_RETURN_IF_ERROR(Bundle::CanEncode(fields, &extension_bits, &total_bits)); |
90 | | |
91 | 3.10k | return writer->WithMaxBits(total_bits, layer, aux_out, [&] { |
92 | 3.10k | WriteVisitor visitor(extension_bits, writer); |
93 | 3.10k | JXL_RETURN_IF_ERROR(visitor.VisitConst(fields)); |
94 | 3.10k | return visitor.OK(); |
95 | 3.10k | }); |
96 | 3.10k | } |
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 | 6.97k | BitWriter* JXL_RESTRICT writer) { |
101 | 6.97k | 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 | 6.97k | writer->Write(bits, value); |
106 | 6.97k | return true; |
107 | 6.97k | } |
108 | | |
109 | | // Returns false if the value is too large to encode. |
110 | | Status U32Coder::Write(const U32Enc enc, const uint32_t value, |
111 | 4.58k | BitWriter* JXL_RESTRICT writer) { |
112 | 4.58k | uint32_t selector; |
113 | 4.58k | size_t total_bits; |
114 | 4.58k | JXL_RETURN_IF_ERROR(ChooseSelector(enc, value, &selector, &total_bits)); |
115 | | |
116 | 4.58k | writer->Write(2, selector); |
117 | | |
118 | 4.58k | const U32Distr d = enc.GetDistr(selector); |
119 | 4.58k | if (!d.IsDirect()) { // Nothing more to write for direct encoding |
120 | 2.15k | const uint32_t offset = d.Offset(); |
121 | 2.15k | JXL_ENSURE(value >= offset); |
122 | 2.15k | writer->Write(total_bits - 2, value - offset); |
123 | 2.15k | } |
124 | | |
125 | 4.58k | return true; |
126 | 4.58k | } |
127 | | |
128 | | // Returns false if the value is too large to encode. |
129 | 906 | Status U64Coder::Write(uint64_t value, BitWriter* JXL_RESTRICT writer) { |
130 | 906 | if (value == 0) { |
131 | | // Selector: use 0 bits, value 0 |
132 | 809 | writer->Write(2, 0); |
133 | 809 | } else if (value <= 16) { |
134 | | // Selector: use 4 bits, value 1..16 |
135 | 97 | writer->Write(2, 1); |
136 | 97 | writer->Write(4, value - 1); |
137 | 97 | } 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 | 906 | return true; |
166 | 906 | } |
167 | | |
168 | 582 | Status F16Coder::Write(float value, BitWriter* JXL_RESTRICT writer) { |
169 | 582 | uint32_t bits32; |
170 | 582 | memcpy(&bits32, &value, sizeof(bits32)); |
171 | 582 | const uint32_t sign = bits32 >> 31; |
172 | 582 | const uint32_t biased_exp32 = (bits32 >> 23) & 0xFF; |
173 | 582 | const uint32_t mantissa32 = bits32 & 0x7FFFFF; |
174 | | |
175 | 582 | const int32_t exp = static_cast<int32_t>(biased_exp32) - 127; |
176 | 582 | 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 | 582 | if (exp < -24) { |
182 | 0 | writer->Write(16, 0); |
183 | 0 | return true; |
184 | 0 | } |
185 | | |
186 | 582 | uint32_t biased_exp16; |
187 | 582 | uint32_t mantissa16; |
188 | | |
189 | | // exp = [-24, -15] => subnormal |
190 | 582 | 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 | 582 | } else { |
196 | | // exp = [-14, 15] |
197 | 582 | biased_exp16 = static_cast<uint32_t>(exp + 15); |
198 | 582 | JXL_ENSURE(1 <= biased_exp16 && biased_exp16 < 31); |
199 | 582 | mantissa16 = mantissa32 >> 13; |
200 | 582 | } |
201 | | |
202 | 582 | JXL_ENSURE(mantissa16 < 1024); |
203 | 582 | const uint32_t bits16 = (sign << 15) | (biased_exp16 << 10) | mantissa16; |
204 | 582 | JXL_ENSURE(bits16 < 0x10000); |
205 | 582 | writer->Write(16, bits16); |
206 | 582 | return true; |
207 | 582 | } |
208 | | |
209 | | Status WriteCodestreamHeaders(CodecMetadata* metadata, BitWriter* writer, |
210 | 213 | AuxOut* aux_out) { |
211 | | // Marker/signature |
212 | 213 | JXL_RETURN_IF_ERROR(writer->WithMaxBits(16, LayerType::Header, aux_out, [&] { |
213 | 213 | writer->Write(8, 0xFF); |
214 | 213 | writer->Write(8, kCodestreamMarker); |
215 | 213 | return true; |
216 | 213 | })); |
217 | | |
218 | 213 | JXL_RETURN_IF_ERROR( |
219 | 213 | WriteSizeHeader(metadata->size, writer, LayerType::Header, aux_out)); |
220 | | |
221 | 213 | JXL_RETURN_IF_ERROR( |
222 | 213 | WriteImageMetadata(metadata->m, writer, LayerType::Header, aux_out)); |
223 | | |
224 | 213 | metadata->transform_data.nonserialized_xyb_encoded = metadata->m.xyb_encoded; |
225 | 213 | JXL_RETURN_IF_ERROR(Bundle::Write(metadata->transform_data, writer, |
226 | 213 | LayerType::Header, aux_out)); |
227 | | |
228 | 213 | return true; |
229 | 213 | } |
230 | | |
231 | | Status WriteFrameHeader(const FrameHeader& frame, |
232 | 283 | BitWriter* JXL_RESTRICT writer, AuxOut* aux_out) { |
233 | 283 | return Bundle::Write(frame, writer, LayerType::Header, aux_out); |
234 | 283 | } |
235 | | |
236 | | Status WriteImageMetadata(const ImageMetadata& metadata, |
237 | | BitWriter* JXL_RESTRICT writer, LayerType layer, |
238 | 213 | AuxOut* aux_out) { |
239 | 213 | return Bundle::Write(metadata, writer, layer, aux_out); |
240 | 213 | } |
241 | | |
242 | | Status WriteQuantizerParams(const QuantizerParams& params, |
243 | | BitWriter* JXL_RESTRICT writer, LayerType layer, |
244 | 186 | AuxOut* aux_out) { |
245 | 186 | return Bundle::Write(params, writer, layer, aux_out); |
246 | 186 | } |
247 | | |
248 | | Status WriteSizeHeader(const SizeHeader& size, BitWriter* JXL_RESTRICT writer, |
249 | 213 | LayerType layer, AuxOut* aux_out) { |
250 | 213 | return Bundle::Write(size, writer, layer, aux_out); |
251 | 213 | } |
252 | | |
253 | | } // namespace jxl |