/src/libjxl/lib/jxl/fields.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_FIELDS_H_ |
7 | | #define LIB_JXL_FIELDS_H_ |
8 | | |
9 | | // Forward/backward-compatible 'bundles' with auto-serialized 'fields'. |
10 | | |
11 | | #include <inttypes.h> |
12 | | #include <stddef.h> |
13 | | #include <stdint.h> |
14 | | #include <stdio.h> |
15 | | #include <stdlib.h> |
16 | | #include <string.h> |
17 | | |
18 | | #include <cinttypes> |
19 | | #include <cmath> // abs |
20 | | #include <cstdarg> |
21 | | |
22 | | #include "lib/jxl/aux_out_fwd.h" |
23 | | #include "lib/jxl/base/bits.h" |
24 | | #include "lib/jxl/base/compiler_specific.h" |
25 | | #include "lib/jxl/base/status.h" |
26 | | #include "lib/jxl/common.h" |
27 | | #include "lib/jxl/dec_bit_reader.h" |
28 | | #include "lib/jxl/enc_bit_writer.h" |
29 | | #include "lib/jxl/field_encodings.h" |
30 | | |
31 | | namespace jxl { |
32 | | |
33 | | // Integer coders: BitsCoder (raw), U32Coder (table), U64Coder (varint). |
34 | | |
35 | | // Reads/writes a given (fixed) number of bits <= 32. |
36 | | class BitsCoder { |
37 | | public: |
38 | 0 | static size_t MaxEncodedBits(const size_t bits) { return bits; } |
39 | | |
40 | | static Status CanEncode(const size_t bits, const uint32_t value, |
41 | 0 | size_t* JXL_RESTRICT encoded_bits) { |
42 | 0 | *encoded_bits = bits; |
43 | 0 | if (value >= (1ULL << bits)) { |
44 | 0 | return JXL_FAILURE("Value %u too large for %" PRIu64 " bits", value, |
45 | 0 | static_cast<uint64_t>(bits)); |
46 | 0 | } |
47 | 0 | return true; |
48 | 0 | } |
49 | | |
50 | 1.19M | static uint32_t Read(const size_t bits, BitReader* JXL_RESTRICT reader) { |
51 | 1.19M | return reader->ReadBits(bits); |
52 | 1.19M | } |
53 | | |
54 | | // Returns false if the value is too large to encode. |
55 | | static Status Write(const size_t bits, const uint32_t value, |
56 | 0 | BitWriter* JXL_RESTRICT writer) { |
57 | 0 | if (value >= (1ULL << bits)) { |
58 | 0 | return JXL_FAILURE("Value %d too large to encode in %" PRIu64 " bits", |
59 | 0 | value, static_cast<uint64_t>(bits)); |
60 | 0 | } |
61 | 0 | writer->Write(bits, value); |
62 | 0 | return true; |
63 | 0 | } |
64 | | }; |
65 | | |
66 | | // Encodes u32 using a lookup table and/or extra bits, governed by a per-field |
67 | | // encoding `enc` which consists of four distributions `d` chosen via a 2-bit |
68 | | // selector (least significant = 0). Each d may have two modes: |
69 | | // - direct: if d.IsDirect(), the value is d.Direct(); |
70 | | // - offset: the value is derived from d.ExtraBits() extra bits plus d.Offset(); |
71 | | // This encoding is denser than Exp-Golomb or Gamma codes when both small and |
72 | | // large values occur. |
73 | | // |
74 | | // Examples: |
75 | | // Direct: U32Enc(Val(8), Val(16), Val(32), Bits(6)), value 32 => 10b. |
76 | | // Offset: U32Enc(Val(0), BitsOffset(1, 1), BitsOffset(2, 3), BitsOffset(8, 8)) |
77 | | // defines the following prefix code: |
78 | | // 00 -> 0 |
79 | | // 01x -> 1..2 |
80 | | // 10xx -> 3..7 |
81 | | // 11xxxxxxxx -> 8..263 |
82 | | class U32Coder { |
83 | | public: |
84 | | static size_t MaxEncodedBits(U32Enc enc); |
85 | | static Status CanEncode(U32Enc enc, uint32_t value, |
86 | | size_t* JXL_RESTRICT encoded_bits); |
87 | | static uint32_t Read(U32Enc enc, BitReader* JXL_RESTRICT reader); |
88 | | |
89 | | // Returns false if the value is too large to encode. |
90 | | static Status Write(U32Enc enc, uint32_t value, |
91 | | BitWriter* JXL_RESTRICT writer); |
92 | | |
93 | | private: |
94 | | static Status ChooseSelector(U32Enc enc, uint32_t value, |
95 | | uint32_t* JXL_RESTRICT selector, |
96 | | size_t* JXL_RESTRICT total_bits); |
97 | | }; |
98 | | |
99 | | // Encodes 64-bit unsigned integers with a fixed distribution, taking 2 bits |
100 | | // to encode 0, 6 bits to encode 1 to 16, 10 bits to encode 17 to 272, 15 bits |
101 | | // to encode up to 4095, and on the order of log2(value) * 1.125 bits for |
102 | | // larger values. |
103 | | class U64Coder { |
104 | | public: |
105 | 0 | static constexpr size_t MaxEncodedBits() { |
106 | 0 | return 2 + 12 + 6 * (8 + 1) + (4 + 1); |
107 | 0 | } |
108 | | |
109 | | static uint64_t Read(BitReader* JXL_RESTRICT reader); |
110 | | |
111 | | // Returns false if the value is too large to encode. |
112 | | static Status Write(uint64_t value, BitWriter* JXL_RESTRICT writer); |
113 | | |
114 | | // Can always encode, but useful because it also returns bit size. |
115 | | static Status CanEncode(uint64_t value, size_t* JXL_RESTRICT encoded_bits); |
116 | | }; |
117 | | |
118 | | // IEEE 754 half-precision (binary16). Refuses to read/write NaN/Inf. |
119 | | class F16Coder { |
120 | | public: |
121 | 0 | static constexpr size_t MaxEncodedBits() { return 16; } |
122 | | |
123 | | // Returns false if the bit representation is NaN or infinity |
124 | | static Status Read(BitReader* JXL_RESTRICT reader, float* JXL_RESTRICT value); |
125 | | |
126 | | // Returns false if the value is too large to encode. |
127 | | static Status Write(float value, BitWriter* JXL_RESTRICT writer); |
128 | | static Status CanEncode(float value, size_t* JXL_RESTRICT encoded_bits); |
129 | | }; |
130 | | |
131 | | // A "bundle" is a forward- and backward compatible collection of fields. |
132 | | // They are used for SizeHeader/FrameHeader/GroupHeader. Bundles can be |
133 | | // extended by appending(!) fields. Optional fields may be omitted from the |
134 | | // bitstream by conditionally visiting them. When reading new bitstreams with |
135 | | // old code, we skip unknown fields at the end of the bundle. This requires |
136 | | // storing the amount of extra appended bits, and that fields are visited in |
137 | | // chronological order of being added to the format, because old decoders |
138 | | // cannot skip some future fields and resume reading old fields. Similarly, |
139 | | // new readers query bits in an "extensions" field to skip (groups of) fields |
140 | | // not present in old bitstreams. Note that each bundle must include an |
141 | | // "extensions" field prior to freezing the format, otherwise it cannot be |
142 | | // extended. |
143 | | // |
144 | | // To ensure interoperability, there will be no opaque fields. |
145 | | // |
146 | | // HOWTO: |
147 | | // - basic usage: define a struct with member variables ("fields") and a |
148 | | // VisitFields(v) member function that calls v->U32/Bool etc. for each |
149 | | // field, specifying their default values. The ctor must call |
150 | | // Bundle::Init(this). |
151 | | // |
152 | | // - print a trace of visitors: ensure each bundle has a static Name() member |
153 | | // function, and change Bundle::Print* to return true. |
154 | | // |
155 | | // - optional fields: in VisitFields, add if (v->Conditional(your_condition)) |
156 | | // { v->Bool(default, &field); }. This prevents reading/writing field |
157 | | // if !your_condition, which is typically computed from a prior field. |
158 | | // WARNING: to ensure all fields are initialized, do not add an else branch; |
159 | | // instead add another if (v->Conditional(!your_condition)). |
160 | | // |
161 | | // - repeated fields: for dynamic sizes, use e.g. std::vector and in |
162 | | // VisitFields, if (v->IsReading()) field.resize(size) before accessing field. |
163 | | // For static or bounded sizes, use an array or std::array. In all cases, |
164 | | // simply visit each array element as if it were a normal field. |
165 | | // |
166 | | // - nested bundles: add a bundle as a normal field and in VisitFields call |
167 | | // JXL_RETURN_IF_ERROR(v->VisitNested(&nested)); |
168 | | // |
169 | | // - allow future extensions: define a "uint64_t extensions" field and call |
170 | | // v->BeginExtensions(&extensions) after visiting all non-extension fields, |
171 | | // and `return v->EndExtensions();` after the last extension field. |
172 | | // |
173 | | // - encode an entire bundle in one bit if ALL its fields equal their default |
174 | | // values: add a "mutable bool all_default" field and as the first visitor: |
175 | | // if (v->AllDefault(*this, &all_default)) { |
176 | | // // Overwrite all serialized fields, but not any nonserialized_*. |
177 | | // v->SetDefault(this); |
178 | | // return true; |
179 | | // } |
180 | | // Note: if extensions are present, AllDefault() == false. |
181 | | |
182 | | class Bundle { |
183 | | public: |
184 | | static constexpr size_t kMaxExtensions = 64; // bits in u64 |
185 | | |
186 | | // Initializes fields to the default values. It is not recursive to nested |
187 | | // fields, this function is intended to be called in the constructors so |
188 | | // each nested field will already Init itself. |
189 | | static void Init(Fields* JXL_RESTRICT fields); |
190 | | |
191 | | // Similar to Init, but recursive to nested fields. |
192 | | static void SetDefault(Fields* JXL_RESTRICT fields); |
193 | | |
194 | | // Returns whether ALL fields (including `extensions`, if present) are equal |
195 | | // to their default value. |
196 | | static bool AllDefault(const Fields& fields); |
197 | | |
198 | | // Returns max number of bits required to encode a T. |
199 | | static size_t MaxBits(const Fields& fields); |
200 | | |
201 | | // Returns whether a header's fields can all be encoded, i.e. they have a |
202 | | // valid representation. If so, "*total_bits" is the exact number of bits |
203 | | // required. Called by Write. |
204 | | static Status CanEncode(const Fields& fields, |
205 | | size_t* JXL_RESTRICT extension_bits, |
206 | | size_t* JXL_RESTRICT total_bits); |
207 | | |
208 | | static Status Read(BitReader* reader, Fields* JXL_RESTRICT fields); |
209 | | |
210 | | // Returns whether enough bits are available to fully read this bundle using |
211 | | // Read. Also returns true in case of a codestream error (other than not being |
212 | | // large enough): that means enough bits are available to determine there's an |
213 | | // error, use Read to get such error status. |
214 | | // NOTE: this advances the BitReader, a different one pointing back at the |
215 | | // original bit position in the codestream must be created to use Read after |
216 | | // this. |
217 | | static bool CanRead(BitReader* reader, Fields* JXL_RESTRICT fields); |
218 | | |
219 | | static Status Write(const Fields& fields, BitWriter* JXL_RESTRICT writer, |
220 | | size_t layer, AuxOut* aux_out); |
221 | | |
222 | | private: |
223 | | }; |
224 | | |
225 | | // Different subclasses of Visitor are passed to implementations of Fields |
226 | | // throughout their lifetime. Templates used to be used for this but dynamic |
227 | | // polymorphism produces more compact executables than template reification did. |
228 | | class Visitor { |
229 | | public: |
230 | 609k | virtual ~Visitor() = default; |
231 | | virtual Status Visit(Fields* fields) = 0; |
232 | | |
233 | | virtual Status Bool(bool default_value, bool* JXL_RESTRICT value) = 0; |
234 | | virtual Status U32(U32Enc, uint32_t, uint32_t*) = 0; |
235 | | |
236 | | // Helper to construct U32Enc from U32Distr. |
237 | | Status U32(const U32Distr d0, const U32Distr d1, const U32Distr d2, |
238 | | const U32Distr d3, const uint32_t default_value, |
239 | 1.89M | uint32_t* JXL_RESTRICT value) { |
240 | 1.89M | return U32(U32Enc(d0, d1, d2, d3), default_value, value); |
241 | 1.89M | } |
242 | | |
243 | | template <typename EnumT> |
244 | 368k | Status Enum(const EnumT default_value, EnumT* JXL_RESTRICT value) { |
245 | 368k | uint32_t u32 = static_cast<uint32_t>(*value); |
246 | | // 00 -> 0 |
247 | | // 01 -> 1 |
248 | | // 10xxxx -> 2..17 |
249 | | // 11yyyyyy -> 18..81 |
250 | 368k | JXL_RETURN_IF_ERROR(U32(Val(0), Val(1), BitsOffset(4, 2), BitsOffset(6, 18), |
251 | 368k | static_cast<uint32_t>(default_value), &u32)); |
252 | 368k | *value = static_cast<EnumT>(u32); |
253 | 368k | return EnumValid(*value); |
254 | 368k | } jxl::Status jxl::Visitor::Enum<jxl::TransferFunction>(jxl::TransferFunction, jxl::TransferFunction*) Line | Count | Source | 244 | 3.25k | Status Enum(const EnumT default_value, EnumT* JXL_RESTRICT value) { | 245 | 3.25k | uint32_t u32 = static_cast<uint32_t>(*value); | 246 | | // 00 -> 0 | 247 | | // 01 -> 1 | 248 | | // 10xxxx -> 2..17 | 249 | | // 11yyyyyy -> 18..81 | 250 | 3.25k | JXL_RETURN_IF_ERROR(U32(Val(0), Val(1), BitsOffset(4, 2), BitsOffset(6, 18), | 251 | 3.25k | static_cast<uint32_t>(default_value), &u32)); | 252 | 3.25k | *value = static_cast<EnumT>(u32); | 253 | 3.25k | return EnumValid(*value); | 254 | 3.25k | } |
jxl::Status jxl::Visitor::Enum<jxl::ColorSpace>(jxl::ColorSpace, jxl::ColorSpace*) Line | Count | Source | 244 | 3.26k | Status Enum(const EnumT default_value, EnumT* JXL_RESTRICT value) { | 245 | 3.26k | uint32_t u32 = static_cast<uint32_t>(*value); | 246 | | // 00 -> 0 | 247 | | // 01 -> 1 | 248 | | // 10xxxx -> 2..17 | 249 | | // 11yyyyyy -> 18..81 | 250 | 3.26k | JXL_RETURN_IF_ERROR(U32(Val(0), Val(1), BitsOffset(4, 2), BitsOffset(6, 18), | 251 | 3.26k | static_cast<uint32_t>(default_value), &u32)); | 252 | 3.25k | *value = static_cast<EnumT>(u32); | 253 | 3.25k | return EnumValid(*value); | 254 | 3.26k | } |
jxl::Status jxl::Visitor::Enum<jxl::WhitePoint>(jxl::WhitePoint, jxl::WhitePoint*) Line | Count | Source | 244 | 3.23k | Status Enum(const EnumT default_value, EnumT* JXL_RESTRICT value) { | 245 | 3.23k | uint32_t u32 = static_cast<uint32_t>(*value); | 246 | | // 00 -> 0 | 247 | | // 01 -> 1 | 248 | | // 10xxxx -> 2..17 | 249 | | // 11yyyyyy -> 18..81 | 250 | 3.23k | JXL_RETURN_IF_ERROR(U32(Val(0), Val(1), BitsOffset(4, 2), BitsOffset(6, 18), | 251 | 3.23k | static_cast<uint32_t>(default_value), &u32)); | 252 | 3.22k | *value = static_cast<EnumT>(u32); | 253 | 3.22k | return EnumValid(*value); | 254 | 3.23k | } |
jxl::Status jxl::Visitor::Enum<jxl::Primaries>(jxl::Primaries, jxl::Primaries*) Line | Count | Source | 244 | 3.17k | Status Enum(const EnumT default_value, EnumT* JXL_RESTRICT value) { | 245 | 3.17k | uint32_t u32 = static_cast<uint32_t>(*value); | 246 | | // 00 -> 0 | 247 | | // 01 -> 1 | 248 | | // 10xxxx -> 2..17 | 249 | | // 11yyyyyy -> 18..81 | 250 | 3.17k | JXL_RETURN_IF_ERROR(U32(Val(0), Val(1), BitsOffset(4, 2), BitsOffset(6, 18), | 251 | 3.17k | static_cast<uint32_t>(default_value), &u32)); | 252 | 3.17k | *value = static_cast<EnumT>(u32); | 253 | 3.17k | return EnumValid(*value); | 254 | 3.17k | } |
jxl::Status jxl::Visitor::Enum<jxl::RenderingIntent>(jxl::RenderingIntent, jxl::RenderingIntent*) Line | Count | Source | 244 | 3.19k | Status Enum(const EnumT default_value, EnumT* JXL_RESTRICT value) { | 245 | 3.19k | uint32_t u32 = static_cast<uint32_t>(*value); | 246 | | // 00 -> 0 | 247 | | // 01 -> 1 | 248 | | // 10xxxx -> 2..17 | 249 | | // 11yyyyyy -> 18..81 | 250 | 3.19k | JXL_RETURN_IF_ERROR(U32(Val(0), Val(1), BitsOffset(4, 2), BitsOffset(6, 18), | 251 | 3.19k | static_cast<uint32_t>(default_value), &u32)); | 252 | 3.19k | *value = static_cast<EnumT>(u32); | 253 | 3.19k | return EnumValid(*value); | 254 | 3.19k | } |
jxl::Status jxl::Visitor::Enum<jxl::ExtraChannel>(jxl::ExtraChannel, jxl::ExtraChannel*) Line | Count | Source | 244 | 352k | Status Enum(const EnumT default_value, EnumT* JXL_RESTRICT value) { | 245 | 352k | uint32_t u32 = static_cast<uint32_t>(*value); | 246 | | // 00 -> 0 | 247 | | // 01 -> 1 | 248 | | // 10xxxx -> 2..17 | 249 | | // 11yyyyyy -> 18..81 | 250 | 352k | JXL_RETURN_IF_ERROR(U32(Val(0), Val(1), BitsOffset(4, 2), BitsOffset(6, 18), | 251 | 352k | static_cast<uint32_t>(default_value), &u32)); | 252 | 352k | *value = static_cast<EnumT>(u32); | 253 | 352k | return EnumValid(*value); | 254 | 352k | } |
|
255 | | |
256 | | virtual Status Bits(size_t bits, uint32_t default_value, |
257 | | uint32_t* JXL_RESTRICT value) = 0; |
258 | | virtual Status U64(uint64_t default_value, uint64_t* JXL_RESTRICT value) = 0; |
259 | | virtual Status F16(float default_value, float* JXL_RESTRICT value) = 0; |
260 | | |
261 | | // Returns whether VisitFields should visit some subsequent fields. |
262 | | // "condition" is typically from prior fields, e.g. flags. |
263 | | // Overridden by InitVisitor and MaxBitsVisitor. |
264 | 218k | virtual Status Conditional(bool condition) { return condition; } |
265 | | |
266 | | // Overridden by InitVisitor, AllDefaultVisitor and CanEncodeVisitor. |
267 | | virtual Status AllDefault(const Fields& /*fields*/, |
268 | 94.4k | bool* JXL_RESTRICT all_default) { |
269 | 94.4k | JXL_RETURN_IF_ERROR(Bool(true, all_default)); |
270 | 94.3k | return *all_default; |
271 | 94.4k | } |
272 | | |
273 | 0 | virtual void SetDefault(Fields* /*fields*/) { |
274 | | // Do nothing by default, this is overridden by ReadVisitor. |
275 | 0 | } |
276 | | |
277 | | // Returns the result of visiting a nested Bundle. |
278 | | // Overridden by InitVisitor. |
279 | 188k | virtual Status VisitNested(Fields* fields) { return Visit(fields); } |
280 | | |
281 | | // Overridden by ReadVisitor. Enables dynamically-sized fields. |
282 | 302k | virtual bool IsReading() const { return false; } |
283 | | |
284 | | virtual Status BeginExtensions(uint64_t* JXL_RESTRICT extensions) = 0; |
285 | | virtual Status EndExtensions() = 0; |
286 | | }; |
287 | | |
288 | | } // namespace jxl |
289 | | |
290 | | #endif // LIB_JXL_FIELDS_H_ |