jxl_image/
lib.rs

1//! This crate provides types related to JPEG XL image headers, such as
2//! [image size information][SizeHeader], [color encoding][ColourEncoding] and
3//! [animation TPS (ticks per second) information][AnimationHeader]. Most of the information is in
4//! the [`ImageMetadata`] struct.
5//!
6//! Image header is at the beginning of the bitstream. One can parse [`ImageHeader`] from the
7//! bitstream to retrieve information about the image.
8use jxl_bitstream::{Bitstream, BitstreamResult, U};
9use jxl_oxide_common::{Bundle, Name, define_bundle};
10
11pub mod color;
12use color::*;
13
14/// JPEG XL image header.
15///
16/// Use [`Bundle::parse`] to parse the header.
17#[derive(Debug)]
18pub struct ImageHeader {
19    /// Image size information.
20    pub size: SizeHeader,
21    /// Image metadata.
22    pub metadata: ImageMetadata,
23}
24
25impl<Ctx> Bundle<Ctx> for ImageHeader {
26    type Error = jxl_bitstream::Error;
27
28    fn parse(bitstream: &mut Bitstream, _: Ctx) -> BitstreamResult<Self> {
29        let signature = bitstream.read_bits(16)?;
30        if signature != 0xaff {
31            return Err(jxl_bitstream::Error::ValidationFailed(
32                "JPEG XL signature mismatch",
33            ));
34        }
35
36        let size = SizeHeader::parse(bitstream, ())?;
37        let metadata = ImageMetadata::parse(bitstream, ())?;
38
39        if metadata.ec_info.len() > 256 {
40            tracing::error!(num_extra = metadata.ec_info.len(), "num_extra too large");
41            return Err(jxl_bitstream::Error::ProfileConformance(
42                "num_extra too large",
43            ));
44        }
45
46        let tone_mapping = &metadata.tone_mapping;
47        if tone_mapping.intensity_target <= 0.0 {
48            return Err(jxl_bitstream::Error::ValidationFailed(
49                "Invalid intensity target",
50            ));
51        }
52        if tone_mapping.min_nits < 0.0 || tone_mapping.min_nits > tone_mapping.intensity_target {
53            return Err(jxl_bitstream::Error::ValidationFailed(
54                "Invalid tone mapping min_nits",
55            ));
56        }
57        if tone_mapping.linear_below < 0.0
58            || (tone_mapping.relative_to_max_display && tone_mapping.linear_below > 1.0)
59        {
60            return Err(jxl_bitstream::Error::ValidationFailed(
61                "Invalid tone mapping linear_below",
62            ));
63        }
64
65        Ok(Self { size, metadata })
66    }
67}
68
69impl ImageHeader {
70    /// Returns the image width with orientation applied.
71    #[inline]
72    pub fn width_with_orientation(&self) -> u32 {
73        self.metadata
74            .apply_orientation(self.size.width, self.size.height, 0, 0, false)
75            .0
76    }
77
78    /// Returns the image height with orientation applied.
79    #[inline]
80    pub fn height_with_orientation(&self) -> u32 {
81        self.metadata
82            .apply_orientation(self.size.width, self.size.height, 0, 0, false)
83            .1
84    }
85}
86
87define_bundle! {
88    /// Image size information.
89    #[derive(Debug)]
90    pub struct SizeHeader {
91        div8: ty(Bool) default(false),
92        h_div8: ty(1 + u(5)) cond(div8) default(0),
93        /// Image height.
94        pub height:
95            ty(U32(1 + u(9), 1 + u(13), 1 + u(18), 1 + u(30))) cond(!div8)
96            default(8 * h_div8),
97        ratio: ty(u(3)) default(0),
98        w_div8: ty(1 + u(5)) cond(div8 && ratio == 0) default(0),
99        /// Image width.
100        pub width:
101            ty(U32(1 + u(9), 1 + u(13), 1 + u(18), 1 + u(30))) cond(!div8 && ratio == 0)
102            default(SizeHeader::compute_default_width(ratio, w_div8, height)),
103    }
104}
105
106impl SizeHeader {
107    fn compute_default_width(ratio: u32, w_div8: u32, height: u32) -> u32 {
108        let height = height as u64;
109        let res = match ratio {
110            0 => 8 * w_div8 as u64,
111            1 => height,
112            2 => height * 12 / 10,
113            3 => height * 4 / 3,
114            4 => height * 3 / 2,
115            5 => height * 16 / 9,
116            6 => height * 5 / 4,
117            7 => height * 2,
118            _ => panic!("Invalid ratio const: {}", ratio),
119        };
120        res as u32
121    }
122}
123
124define_bundle! {
125    /// Image metadata.
126    #[derive(Debug)]
127    pub struct ImageMetadata {
128        all_default: ty(Bool) default(true),
129        extra_fields: ty(Bool) cond(!all_default) default(false),
130        /// Value representing image orientation.
131        pub orientation: ty(1 + u(3)) cond(extra_fields) default(1),
132        have_intr_size: ty(Bool) cond(extra_fields) default(false),
133        /// Recommended size to display the image.
134        pub intrinsic_size: ty(Bundle(Option<SizeHeader>)) cond(have_intr_size),
135        have_preview: ty(Bool) cond(extra_fields) default(false),
136        /// Size information of the preview frame, if there is any.
137        pub preview: ty(Bundle(Option<PreviewHeader>)) cond(have_preview),
138        have_animation: ty(Bool) cond(extra_fields) default(false),
139        /// Information about the animation such as TPS, if the image is animated.
140        pub animation: ty(Bundle(Option<AnimationHeader>)) cond(have_animation),
141        /// Bit depth information, which is used to parse Modular image samples.
142        pub bit_depth: ty(Bundle(BitDepth)) cond(!all_default),
143        /// Whether 16-bit buffer is sufficient to correctly parse Modular images.
144        pub modular_16bit_buffers: ty(Bool) cond(!all_default) default(true),
145        num_extra: ty(U32(0, 1, 2 + u(4), 1 + u(12))) cond(!all_default) default(0),
146        /// Information about extra channels, such as alpha and black channels.
147        pub ec_info: ty(Vec[Bundle(ExtraChannelInfo)]; num_extra) cond(!all_default),
148        /// Whether the image is encoded in XYB color space.
149        pub xyb_encoded: ty(Bool) cond(!all_default) default(true),
150        /// Color encoding of the image.
151        ///
152        /// If `xyb_encoded` is `true`, this is a suggestion of the color space to present the
153        /// decoded image. If it's not, the decoded image is in the color space represented by this
154        /// field.
155        pub colour_encoding: ty(Bundle(ColourEncoding)) cond(!all_default),
156        /// Tone mapping information, which is used to map HDR images to SDR.
157        pub tone_mapping: ty(Bundle(ToneMapping)) cond(extra_fields),
158        pub extensions: ty(Bundle(Extensions)) cond(!all_default),
159        default_m: ty(Bool),
160        /// Opsin inverse matrix, which is used to transform XYB encoded image to sRGB color space.
161        pub opsin_inverse_matrix: ty(Bundle(OpsinInverseMatrix)) cond(!default_m && xyb_encoded),
162        cw_mask: ty(u(3)) cond(!default_m) default(0),
163        /// 2x upsampling weights.
164        pub up2_weight: ty(Array[F16]; 15) cond(cw_mask & 1 != 0) default(Self::D_UP2),
165        /// 4x upsampling weights.
166        pub up4_weight: ty(Array[F16]; 55) cond(cw_mask & 2 != 0) default(Self::D_UP4),
167        /// 8x upsampling weights.
168        pub up8_weight: ty(Array[F16]; 210) cond(cw_mask & 4 != 0) default(Self::D_UP8),
169    }
170
171    #[derive(Debug)]
172    pub struct PreviewHeader {
173        div8: ty(Bool),
174        h_div8: ty(U32(16, 32, 1 + u(5), 33 + u(9))) cond(div8) default(1),
175        /// Height of the preview image.
176        pub height:
177            ty(U32(1 + u(6), 65 + u(8), 321 + u(10), 1345 + u(12))) cond(!div8)
178            default(8 * h_div8),
179        ratio: ty(u(3)),
180        w_div8: ty(U32(16, 32, 1 + u(5), 33 + u(9))) cond(div8) default(1),
181        /// Width of the preview image.
182        pub width:
183            ty(U32(1 + u(6), 65 + u(8), 321 + u(10), 1345 + u(12))) cond(!div8)
184            default(SizeHeader::compute_default_width(ratio, w_div8, height)),
185    }
186
187    /// Animation information.
188    ///
189    /// TPS (ticks per second) is computed as `tps_numerator / tps_denominator`, which means
190    /// `tps_denominator / tps_numerator` seconds per tick.
191    #[derive(Debug)]
192    pub struct AnimationHeader {
193        /// TPS numerator.
194        pub tps_numerator: ty(U32(100, 1000, 1 + u(10), 1 + u(30))) default(0),
195        /// TPS denominator.
196        pub tps_denominator: ty(U32(1, 1001, 1 + u(8), 1 + u(10))) default(0),
197        /// Number of loops, where 0 means it loops forever.
198        pub num_loops: ty(U32(0, u(3), u(16), u(32))) default(0),
199        /// Whether keyframes in the image has their timecodes embedded.
200        pub have_timecodes: ty(Bool) default(false),
201    }
202}
203
204#[derive(Debug, Default)]
205#[allow(unused)]
206pub struct Extensions {
207    extension_bits: u64,
208}
209
210impl<Ctx> Bundle<Ctx> for Extensions {
211    type Error = jxl_bitstream::Error;
212
213    fn parse(bitstream: &mut Bitstream, _: Ctx) -> jxl_bitstream::BitstreamResult<Self> {
214        let extension_bits = bitstream.read_u64()?;
215        let mut bits = extension_bits;
216        let mut extension_data_bitlen = Vec::with_capacity(extension_bits.count_ones() as usize);
217        for extension_idx in 0..64 {
218            if bits & 1 != 0 {
219                tracing::warn!(extension_idx, "Unknown extension");
220                extension_data_bitlen.push(bitstream.read_u64()?);
221            }
222            bits >>= 1;
223        }
224
225        for len in extension_data_bitlen {
226            bitstream.skip_bits(len as usize)?;
227        }
228
229        Ok(Self { extension_bits })
230    }
231}
232
233impl ImageMetadata {
234    /// Returns whether the image is grayscale.
235    #[inline]
236    pub fn grayscale(&self) -> bool {
237        self.colour_encoding.colour_space() == ColourSpace::Grey
238    }
239
240    /// Returns the index of the first alpha channel in the image.
241    pub fn alpha(&self) -> Option<usize> {
242        self.ec_info
243            .iter()
244            .position(|info| matches!(info.ty, ExtraChannelType::Alpha { .. }))
245    }
246
247    /// Returns where the given coordinate will be placed after the orientation is applied.
248    #[inline]
249    pub fn apply_orientation(
250        &self,
251        width: u32,
252        height: u32,
253        left: i32,
254        top: i32,
255        inverse: bool,
256    ) -> (u32, u32, i32, i32) {
257        let (left, top) = match self.orientation {
258            1 => (left, top),
259            2 => (width as i32 - left - 1, top),
260            3 => (width as i32 - left - 1, height as i32 - top - 1),
261            4 => (left, height as i32 - top - 1),
262            5 => (top, left),
263            6 if inverse => (top, width as i32 - left - 1),
264            6 => (height as i32 - top - 1, left),
265            7 => (height as i32 - top - 1, width as i32 - left - 1),
266            8 if inverse => (height as i32 - top - 1, left),
267            8 => (top, width as i32 - left - 1),
268            _ => unreachable!(),
269        };
270        let (width, height) = match self.orientation {
271            1..=4 => (width, height),
272            5..=8 => (height, width),
273            _ => unreachable!(),
274        };
275        (width, height, left, top)
276    }
277}
278
279/// Information about an extra channel.
280#[derive(Debug, Default, Clone)]
281pub struct ExtraChannelInfo {
282    /// Type and associated parameters of the channel.
283    pub ty: ExtraChannelType,
284    /// Bit depth information about the channel.
285    pub bit_depth: BitDepth,
286    /// `dim_shift` used to decode Modular image.
287    pub dim_shift: u32,
288    /// Name of the channel.
289    pub name: Name,
290}
291
292impl<Ctx> Bundle<Ctx> for ExtraChannelInfo {
293    type Error = jxl_bitstream::Error;
294
295    fn parse(bitstream: &mut Bitstream, _: Ctx) -> BitstreamResult<Self> {
296        let default_alpha_channel = bitstream.read_bool()?;
297        if default_alpha_channel {
298            return Ok(Self::default());
299        }
300
301        let ty_id = bitstream.read_enum::<ExtraChannelTypeRaw>()?;
302        let bit_depth = BitDepth::parse(bitstream, ())?;
303        let dim_shift = bitstream.read_u32(0, 3, 4, 1 + U(3))?;
304        let name = Name::parse(bitstream, ())?;
305
306        let ty = match ty_id {
307            ExtraChannelTypeRaw::Alpha => ExtraChannelType::Alpha {
308                alpha_associated: bitstream.read_bool()?,
309            },
310            ExtraChannelTypeRaw::Depth => ExtraChannelType::Depth,
311            ExtraChannelTypeRaw::SpotColour => ExtraChannelType::SpotColour {
312                red: bitstream.read_f16_as_f32()?,
313                green: bitstream.read_f16_as_f32()?,
314                blue: bitstream.read_f16_as_f32()?,
315                solidity: bitstream.read_f16_as_f32()?,
316            },
317            ExtraChannelTypeRaw::SelectionMask => ExtraChannelType::SelectionMask,
318            ExtraChannelTypeRaw::Black => ExtraChannelType::Black,
319            ExtraChannelTypeRaw::Cfa => ExtraChannelType::Cfa {
320                cfa_channel: bitstream.read_u32(1, U(2), 3 + U(4), 19 + U(8))?,
321            },
322            ExtraChannelTypeRaw::Thermal => ExtraChannelType::Thermal,
323            ExtraChannelTypeRaw::NonOptional => ExtraChannelType::NonOptional,
324            ExtraChannelTypeRaw::Optional => ExtraChannelType::Optional,
325        };
326
327        Ok(Self {
328            ty,
329            bit_depth,
330            dim_shift,
331            name,
332        })
333    }
334}
335
336impl ExtraChannelInfo {
337    /// Returns whether this is an alpha channel.
338    #[inline]
339    pub fn is_alpha(&self) -> bool {
340        matches!(self.ty, ExtraChannelType::Alpha { .. })
341    }
342
343    /// Returns whether the alpha channel has premultiplied semantics.
344    #[inline]
345    pub fn alpha_associated(&self) -> Option<bool> {
346        if let ExtraChannelType::Alpha { alpha_associated } = self.ty {
347            Some(alpha_associated)
348        } else {
349            None
350        }
351    }
352
353    /// Returns whether this is a black channel of a CMYK image.
354    #[inline]
355    pub fn is_black(&self) -> bool {
356        self.ty == ExtraChannelType::Black
357    }
358}
359
360/// Type of an extra channel.
361#[derive(Debug, PartialEq, Copy, Clone)]
362#[repr(u8)]
363pub enum ExtraChannelType {
364    Alpha {
365        alpha_associated: bool,
366    } = 0,
367    Depth,
368    SpotColour {
369        red: f32,
370        green: f32,
371        blue: f32,
372        solidity: f32,
373    },
374    SelectionMask,
375    Black,
376    Cfa {
377        cfa_channel: u32,
378    },
379    Thermal,
380    NonOptional = 15,
381    Optional,
382}
383
384impl Default for ExtraChannelType {
385    fn default() -> Self {
386        Self::Alpha {
387            alpha_associated: false,
388        }
389    }
390}
391
392#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
393#[repr(u8)]
394enum ExtraChannelTypeRaw {
395    Alpha = 0,
396    Depth,
397    SpotColour,
398    SelectionMask,
399    Black,
400    Cfa,
401    Thermal,
402    NonOptional = 15,
403    Optional,
404}
405
406impl TryFrom<u32> for ExtraChannelTypeRaw {
407    type Error = ();
408
409    fn try_from(value: u32) -> std::result::Result<Self, Self::Error> {
410        Ok(match value {
411            0 => Self::Alpha,
412            1 => Self::Depth,
413            2 => Self::SpotColour,
414            3 => Self::SelectionMask,
415            4 => Self::Black,
416            5 => Self::Cfa,
417            6 => Self::Thermal,
418            15 => Self::NonOptional,
419            16 => Self::Optional,
420            _ => return Err(()),
421        })
422    }
423}
424
425/// Bit depth information.
426#[derive(Debug, Copy, Clone, PartialEq, Eq)]
427pub enum BitDepth {
428    /// Modular image samples represent integer values, where the range
429    /// `0..=(1 << bits_per_sample) - 1` corresponds to \[0.0, 1.0\], scaled linearly.
430    ///
431    /// The value outside of the \[0.0, 1.0\] range is *not* clamped.
432    IntegerSample { bits_per_sample: u32 },
433    /// Modular image samples represent bitcast of floating point values with a sign bit,
434    /// `exp_bits` exponential bits, and the remaining mantissa bits.
435    FloatSample { bits_per_sample: u32, exp_bits: u32 },
436}
437
438impl Default for BitDepth {
439    fn default() -> Self {
440        Self::IntegerSample { bits_per_sample: 8 }
441    }
442}
443
444impl BitDepth {
445    #[inline]
446    pub fn bits_per_sample(self) -> u32 {
447        match self {
448            Self::IntegerSample { bits_per_sample } => bits_per_sample,
449            Self::FloatSample {
450                bits_per_sample, ..
451            } => bits_per_sample,
452        }
453    }
454
455    /// Parses the given Modular image sample to an `f32`.
456    #[inline]
457    pub fn parse_integer_sample(self, sample: i32) -> f32 {
458        match self {
459            Self::IntegerSample { bits_per_sample } => {
460                let div = (1i32 << bits_per_sample) - 1;
461                sample as f32 / div as f32
462            }
463            Self::FloatSample {
464                bits_per_sample,
465                exp_bits,
466            } => {
467                let sample = sample as u32;
468                let mantissa_bits = bits_per_sample - exp_bits - 1;
469                let mantissa_mask = (1u32 << mantissa_bits) - 1;
470                let exp_mask = ((1u32 << (bits_per_sample - 1)) - 1) ^ mantissa_mask;
471
472                let is_signed = (sample & (1u32 << (bits_per_sample - 1))) != 0;
473                let mantissa = sample & mantissa_mask;
474                let exp = ((sample & exp_mask) >> mantissa_bits) as i32;
475                let exp = exp - ((1 << (exp_bits - 1)) - 1);
476
477                // TODO: handle subnormal values.
478                let f32_mantissa_bits = f32::MANTISSA_DIGITS - 1;
479                let mantissa = match mantissa_bits.cmp(&f32_mantissa_bits) {
480                    std::cmp::Ordering::Less => mantissa << (f32_mantissa_bits - mantissa_bits),
481                    std::cmp::Ordering::Greater => mantissa >> (mantissa_bits - f32_mantissa_bits),
482                    _ => mantissa,
483                };
484                let exp = (exp + 127) as u32;
485                let sign = is_signed as u32;
486
487                let bits = (sign << 31) | (exp << f32_mantissa_bits) | mantissa;
488                f32::from_bits(bits)
489            }
490        }
491    }
492}
493
494impl<Ctx> Bundle<Ctx> for BitDepth {
495    type Error = jxl_bitstream::Error;
496
497    fn parse(bitstream: &mut Bitstream, _ctx: Ctx) -> BitstreamResult<Self> {
498        if bitstream.read_bool()? {
499            // float_sample
500            let bits_per_sample = bitstream.read_u32(32, 16, 24, 1 + U(6))?;
501            let exp_bits = bitstream.read_bits(4)? + 1;
502            if !(2..=8).contains(&exp_bits) {
503                return Err(jxl_bitstream::Error::ValidationFailed(
504                    "Invalid exp_bits per float sample",
505                ));
506            }
507            let mantissa_bits = bits_per_sample.wrapping_sub(exp_bits + 1);
508            if !(2..=23).contains(&mantissa_bits) {
509                return Err(jxl_bitstream::Error::ValidationFailed(
510                    "Invalid mantissa_bits per float sample",
511                ));
512            }
513            Ok(Self::FloatSample {
514                bits_per_sample,
515                exp_bits,
516            })
517        } else {
518            let bits_per_sample = bitstream.read_u32(8, 10, 12, 1 + U(6))?;
519            if bits_per_sample > 31 {
520                return Err(jxl_bitstream::Error::ValidationFailed(
521                    "Invalid bits_per_sample",
522                ));
523            }
524            Ok(Self::IntegerSample { bits_per_sample })
525        }
526    }
527}
528
529#[allow(clippy::excessive_precision)]
530#[rustfmt::skip]
531impl ImageMetadata {
532    const D_UP2: [f32; 15] = [
533        -0.01716200, -0.03452303, -0.04022174, -0.02921014, -0.00624645,
534        0.14111091, 0.28896755, 0.00278718, -0.01610267, 0.56661550,
535        0.03777607, -0.01986694, -0.03144731, -0.01185068, -0.00213539,
536    ];
537    const D_UP4: [f32; 55] = [
538        -0.02419067, -0.03491987, -0.03693351, -0.03094285, -0.00529785,
539        -0.01663432, -0.03556863, -0.03888905, -0.03516850, -0.00989469,
540        0.23651958, 0.33392945, -0.01073543, -0.01313181, -0.03556694,
541        0.13048175, 0.40103025, 0.03951150, -0.02077584, 0.46914198,
542        -0.00209270, -0.01484589, -0.04064806, 0.18942530, 0.56279892,
543        0.06674400, -0.02335494, -0.03551682, -0.00754830, -0.02267919,
544        -0.02363578, 0.00315804, -0.03399098, -0.01359519, -0.00091653,
545        -0.00335467, -0.01163294, -0.01610294, -0.00974088, -0.00191622,
546        -0.01095446, -0.03198464, -0.04455121, -0.02799790, -0.00645912,
547        0.06390599, 0.22963888, 0.00630981, -0.01897349, 0.67537268,
548        0.08483369, -0.02534994, -0.02205197, -0.01667999, -0.00384443,
549    ];
550    const D_UP8: [f32; 210] = [
551        -0.02928613, -0.03706353, -0.03783812, -0.03324558, -0.00447632,
552        -0.02519406, -0.03752601, -0.03901508, -0.03663285, -0.00646649,
553        -0.02066407, -0.03838633, -0.04002101, -0.03900035, -0.00901973,
554        -0.01626393, -0.03954148, -0.04046620, -0.03979621, -0.01224485,
555        0.29895328, 0.35757708, -0.02447552, -0.01081748, -0.04314594,
556        0.23903219, 0.41119301, -0.00573046, -0.01450239, -0.04246845,
557        0.17567618, 0.45220643, 0.02287757, -0.01936783, -0.03583255,
558        0.11572472, 0.47416733, 0.06284440, -0.02685066, 0.42720050,
559        -0.02248939, -0.01155273, -0.04562755, 0.28689496, 0.49093869,
560        -0.00007891, -0.01545926, -0.04562659, 0.21238920, 0.53980934,
561        0.03369474, -0.02070211, -0.03866988, 0.14229550, 0.56593398,
562        0.08045181, -0.02888298, -0.03680918, -0.00542229, -0.02920477,
563        -0.02788574, -0.02118180, -0.03942402, -0.00775547, -0.02433614,
564        -0.03193943, -0.02030828, -0.04044014, -0.01074016, -0.01930822,
565        -0.03620399, -0.01974125, -0.03919545, -0.01456093, -0.00045072,
566        -0.00360110, -0.01020207, -0.01231907, -0.00638988, -0.00071592,
567        -0.00279122, -0.00957115, -0.01288327, -0.00730937, -0.00107783,
568        -0.00210156, -0.00890705, -0.01317668, -0.00813895, -0.00153491,
569        -0.02128481, -0.04173044, -0.04831487, -0.03293190, -0.00525260,
570        -0.01720322, -0.04052736, -0.05045706, -0.03607317, -0.00738030,
571        -0.01341764, -0.03965629, -0.05151616, -0.03814886, -0.01005819,
572        0.18968273, 0.33063684, -0.01300105, -0.01372950, -0.04017465,
573        0.13727832, 0.36402234, 0.01027890, -0.01832107, -0.03365072,
574        0.08734506, 0.38194295, 0.04338228, -0.02525993, 0.56408126,
575        0.00458352, -0.01648227, -0.04887868, 0.24585519, 0.62026135,
576        0.04314807, -0.02213737, -0.04158014, 0.16637289, 0.65027023,
577        0.09621636, -0.03101388, -0.04082742, -0.00904519, -0.02790922,
578        -0.02117818, 0.00798662, -0.03995711, -0.01243427, -0.02231705,
579        -0.02946266, 0.00992055, -0.03600283, -0.01684920, -0.00111684,
580        -0.00411204, -0.01297130, -0.01723725, -0.01022545, -0.00165306,
581        -0.00313110, -0.01218016, -0.01763266, -0.01125620, -0.00231663,
582        -0.01374149, -0.03797620, -0.05142937, -0.03117307, -0.00581914,
583        -0.01064003, -0.03608089, -0.05272168, -0.03375670, -0.00795586,
584        0.09628104, 0.27129991, -0.00353779, -0.01734151, -0.03153981,
585        0.05686230, 0.28500998, 0.02230594, -0.02374955, 0.68214326,
586        0.05018048, -0.02320852, -0.04383616, 0.18459474, 0.71517975,
587        0.10805613, -0.03263677, -0.03637639, -0.01394373, -0.02511203,
588        -0.01728636, 0.05407331, -0.02867568, -0.01893131, -0.00240854,
589        -0.00446511, -0.01636187, -0.02377053, -0.01522848, -0.00333334,
590        -0.00819975, -0.02964169, -0.04499287, -0.02745350, -0.00612408,
591        0.02727416, 0.19446600, 0.00159832, -0.02232473, 0.74982506,
592        0.11452620, -0.03348048, -0.01605681, -0.02070339, -0.00458223,
593    ];
594}