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