1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
/*
 * Copyright (c) 2023.
 *
 * This software is free software;
 *
 * You can redistribute it or modify it under terms of the MIT, Apache License or Zlib license
 */

//! Global Decoder options
#![allow(clippy::zero_prefixed_literal)]

use crate::bit_depth::ByteEndian;
use crate::colorspace::ColorSpace;

fn decoder_strict_mode() -> DecoderFlags {
    DecoderFlags {
        inflate_confirm_adler:        true,
        png_confirm_crc:              true,
        jpg_error_on_non_conformance: true,

        zune_use_unsafe:           true,
        zune_use_neon:             true,
        zune_use_avx:              true,
        zune_use_avx2:             true,
        zune_use_sse2:             true,
        zune_use_sse3:             true,
        zune_use_sse41:            true,
        png_add_alpha_channel:     false,
        png_strip_16_bit_to_8_bit: false,
        png_decode_animated:       true,
        jxl_decode_animated:       true
    }
}

/// Fast decoder options
///
/// Enables all intrinsics + unsafe routines
///
/// Disables png adler and crc checking.
fn fast_options() -> DecoderFlags {
    DecoderFlags {
        inflate_confirm_adler:        false,
        png_confirm_crc:              false,
        jpg_error_on_non_conformance: false,

        zune_use_unsafe: true,
        zune_use_neon:   true,
        zune_use_avx:    true,
        zune_use_avx2:   true,
        zune_use_sse2:   true,
        zune_use_sse3:   true,
        zune_use_sse41:  true,

        png_add_alpha_channel:     false,
        png_strip_16_bit_to_8_bit: false,
        png_decode_animated:       true,
        jxl_decode_animated:       true
    }
}

/// Command line options error resilient and fast
///
/// Features
/// - Ignore CRC and Adler in png
/// - Do not error out on non-conformance in jpg
/// - Use unsafe paths
fn cmd_options() -> DecoderFlags {
    DecoderFlags {
        inflate_confirm_adler:        false,
        png_confirm_crc:              false,
        jpg_error_on_non_conformance: false,

        zune_use_unsafe: true,
        zune_use_neon:   true,
        zune_use_avx:    true,
        zune_use_avx2:   true,
        zune_use_sse2:   true,
        zune_use_sse3:   true,
        zune_use_sse41:  true,

        png_add_alpha_channel:     false,
        png_strip_16_bit_to_8_bit: false,

        png_decode_animated: true,
        jxl_decode_animated: true
    }
}

/// Decoder options that are flags
///
/// NOTE: When you extend this, add true or false to
/// all options above that return a `DecoderFlag`
#[derive(Copy, Debug, Clone, Default)]
pub struct DecoderFlags {
    /// Whether the decoder should confirm and report adler mismatch
    inflate_confirm_adler:        bool,
    /// Whether the PNG decoder should confirm crc
    png_confirm_crc:              bool,
    /// Whether the png decoder should error out on image non-conformance
    jpg_error_on_non_conformance: bool,
    /// Whether the decoder should use unsafe  platform specific intrinsics
    ///
    /// This will also shut down platform specific intrinsics `(ZUNE_USE_{EXT})` value
    zune_use_unsafe:              bool,
    /// Whether we should use SSE2.
    ///
    /// This should be enabled for all x64 platforms but can be turned off if
    /// `ZUNE_USE_UNSAFE` is false
    zune_use_sse2:                bool,
    /// Whether we should use SSE3 instructions where possible.
    zune_use_sse3:                bool,
    /// Whether we should use sse4.1 instructions where possible.
    zune_use_sse41:               bool,
    /// Whether we should use avx instructions where possible.
    zune_use_avx:                 bool,
    /// Whether we should use avx2 instructions where possible.
    zune_use_avx2:                bool,
    /// Whether the png decoder should add alpha channel where possible.
    png_add_alpha_channel:        bool,
    /// Whether we should use neon instructions where possible.
    zune_use_neon:                bool,
    /// Whether the png decoder should strip 16 bit to 8 bit
    png_strip_16_bit_to_8_bit:    bool,
    /// Decode all frames for an animated images
    png_decode_animated:          bool,
    jxl_decode_animated:          bool
}

/// Decoder options
///
/// Not all options are respected by decoders all decoders
#[derive(Debug, Copy, Clone)]
pub struct DecoderOptions {
    /// Maximum width for which decoders will
    /// not try to decode images larger than
    /// the specified width.
    ///
    /// - Default value: 16384
    /// - Respected by: `all decoders`
    max_width:      usize,
    /// Maximum height for which decoders will not
    /// try to decode images larger than the
    /// specified height
    ///
    /// - Default value: 16384
    /// - Respected by: `all decoders`
    max_height:     usize,
    /// Output colorspace
    ///
    /// The jpeg decoder allows conversion to a separate colorspace
    /// than the input.
    ///
    /// I.e you can convert a RGB jpeg image to grayscale without
    /// first decoding it to RGB to get
    ///
    /// - Default value: `ColorSpace::RGB`
    /// - Respected by: `jpeg`
    out_colorspace: ColorSpace,

    /// Maximum number of scans allowed
    /// for progressive jpeg images
    ///
    /// Progressive jpegs have scans
    ///
    /// - Default value:100
    /// - Respected by: `jpeg`
    max_scans:     usize,
    /// Maximum size for deflate.
    /// Respected by all decoders that use inflate/deflate
    deflate_limit: usize,
    /// Boolean flags that influence decoding
    flags:         DecoderFlags,
    /// The byte endian of the returned bytes will be stored in
    /// in case a single pixel spans more than a byte
    endianness:    ByteEndian
}

/// Initializers
impl DecoderOptions {
    /// Create the decoder with options  setting most configurable
    /// options to be their safe counterparts
    ///
    /// This is the same as `default` option as default initializes
    /// options to the  safe variant.
    ///
    /// Note, decoders running on this will be slower as it disables
    /// platform specific intrinsics
    pub fn new_safe() -> DecoderOptions {
        DecoderOptions::default()
    }

    /// Create the decoder with options setting the configurable options
    /// to the fast  counterparts
    ///
    /// This enables platform specific code paths and enable use of unsafe
    pub fn new_fast() -> DecoderOptions {
        let flag = fast_options();
        DecoderOptions::default().set_decoder_flags(flag)
    }

    /// Create the decoder options with the following characteristics
    ///
    /// - Use unsafe paths.
    /// - Ignore error checksuming, e.g in png we do not confirm adler and crc in this mode
    /// - Enable fast intrinsics paths
    pub fn new_cmd() -> DecoderOptions {
        let flag = cmd_options();
        DecoderOptions::default().set_decoder_flags(flag)
    }
}

/// Global options respected by all decoders
impl DecoderOptions {
    /// Get maximum width configured for which the decoder
    /// should not try to decode images greater than this width
    pub const fn get_max_width(&self) -> usize {
        self.max_width
    }

    /// Get maximum height configured for which the decoder should
    /// not try to decode images greater than this height
    pub const fn get_max_height(&self) -> usize {
        self.max_height
    }

    /// Return true whether the decoder should be in strict mode
    /// And reject most errors
    pub fn get_strict_mode(&self) -> bool {
        self.flags.jpg_error_on_non_conformance
            | self.flags.png_confirm_crc
            | self.flags.inflate_confirm_adler
    }
    /// Return true if the decoder should use unsafe
    /// routines where possible
    pub const fn get_use_unsafe(&self) -> bool {
        self.flags.zune_use_unsafe
    }

    /// Set maximum width for which the decoder should not try
    /// decoding images greater than that width
    ///
    /// # Arguments
    ///
    /// * `width`:  The maximum width allowed
    ///
    /// returns: DecoderOptions
    pub fn set_max_width(mut self, width: usize) -> Self {
        self.max_width = width;
        self
    }

    /// Set maximum height for which the decoder should not try
    /// decoding images greater than that height
    /// # Arguments
    ///
    /// * `height`: The maximum height allowed
    ///
    /// returns: DecoderOptions
    ///
    pub fn set_max_height(mut self, height: usize) -> Self {
        self.max_height = height;
        self
    }

    /// Whether the routines can use unsafe platform specific
    /// intrinsics when necessary
    ///
    /// Platform intrinsics are implemented for operations which
    /// the compiler can't auto-vectorize, or we can do a marginably
    /// better job at it
    ///
    /// All decoders with unsafe routines respect it.
    ///
    /// Treat this with caution, disabling it will cause slowdowns but
    /// it's provided for mainly for debugging use.
    ///
    /// - Respected by: `png` and `jpeg`(decoders with unsafe routines)
    pub fn set_use_unsafe(mut self, yes: bool) -> Self {
        // first clear the flag
        self.flags.zune_use_unsafe = yes;
        self
    }

    fn set_decoder_flags(mut self, flags: DecoderFlags) -> Self {
        self.flags = flags;
        self
    }
    /// Set whether the decoder should be in standards conforming/
    /// strict mode
    ///
    /// This reduces the error tolerance level for the decoders and invalid
    /// samples will be rejected by the decoder
    ///
    /// # Arguments
    ///
    /// * `yes`:
    ///
    /// returns: DecoderOptions
    ///
    pub fn set_strict_mode(mut self, yes: bool) -> Self {
        self.flags.jpg_error_on_non_conformance = yes;
        self.flags.png_confirm_crc = yes;
        self.flags.inflate_confirm_adler = yes;
        self
    }

    /// Set the byte endian for which raw samples will be stored in
    /// in case a single pixel sample spans more than a byte.
    ///
    /// The default is usually native endian hence big endian values
    /// will be converted to little endian on little endian systems,
    ///
    /// and little endian values will be converted to big endian on big endian systems
    ///
    /// # Arguments
    ///
    /// * `endian`: The endianness to which to set the bytes to
    ///
    /// returns: DecoderOptions
    pub fn set_byte_endian(mut self, endian: ByteEndian) -> Self {
        self.endianness = endian;
        self
    }

    /// Get the byte endian for which samples that span more than one byte will
    /// be treated
    pub const fn get_byte_endian(&self) -> ByteEndian {
        self.endianness
    }
}

/// PNG specific options
impl DecoderOptions {
    /// Whether the inflate decoder should confirm
    /// adler checksums
    pub const fn inflate_get_confirm_adler(&self) -> bool {
        self.flags.inflate_confirm_adler
    }
    /// Set whether the inflate decoder should confirm
    /// adler checksums
    pub fn inflate_set_confirm_adler(mut self, yes: bool) -> Self {
        self.flags.inflate_confirm_adler = yes;
        self
    }
    /// Get default inflate limit for which the decoder
    /// will not try to decompress further
    pub const fn inflate_get_limit(&self) -> usize {
        self.deflate_limit
    }
    /// Set the default inflate limit for which decompressors
    /// relying on inflate won't surpass this limit
    #[must_use]
    pub fn inflate_set_limit(mut self, limit: usize) -> Self {
        self.deflate_limit = limit;
        self
    }
    /// Whether the inflate decoder should confirm
    /// crc 32 checksums
    pub const fn png_get_confirm_crc(&self) -> bool {
        self.flags.png_confirm_crc
    }
    /// Set whether the png decoder should confirm
    /// CRC 32 checksums
    #[must_use]
    pub fn png_set_confirm_crc(mut self, yes: bool) -> Self {
        self.flags.png_confirm_crc = yes;
        self
    }
    /// Set whether the png decoder should add an alpha channel to
    /// images where possible.
    ///
    /// For Luma images, it converts it to Luma+Alpha
    ///
    /// For RGB images it converts it to RGB+Alpha
    pub fn png_set_add_alpha_channel(mut self, yes: bool) -> Self {
        self.flags.png_add_alpha_channel = yes;
        self
    }
    /// Return true whether the png decoder should add an alpha
    /// channel to images where possible
    pub const fn png_get_add_alpha_channel(&self) -> bool {
        self.flags.png_add_alpha_channel
    }

    /// Whether the png decoder should reduce 16 bit images to 8 bit
    /// images implicitly.
    ///
    /// Equivalent to [png::Transformations::STRIP_16](https://docs.rs/png/latest/png/struct.Transformations.html#associatedconstant.STRIP_16)
    pub fn png_set_strip_to_8bit(mut self, yes: bool) -> Self {
        self.flags.png_strip_16_bit_to_8_bit = yes;
        self
    }

    /// Return a boolean indicating whether the png decoder should reduce
    /// 16 bit images to 8 bit images implicitly
    pub const fn png_get_strip_to_8bit(&self) -> bool {
        self.flags.png_strip_16_bit_to_8_bit
    }

    /// Return whether `zune-image` should decode animated images or
    /// whether we should just decode the first frame only
    pub const fn png_decode_animated(&self) -> bool {
        self.flags.png_decode_animated
    }
    /// Set  whether `zune-image` should decode animated images or
    /// whether we should just decode the first frame only
    pub const fn png_set_decode_animated(mut self, yes: bool) -> Self {
        self.flags.png_decode_animated = yes;
        self
    }
}

/// JPEG specific options
impl DecoderOptions {
    /// Get maximum scans for which the jpeg decoder
    /// should not go above for progressive images
    pub const fn jpeg_get_max_scans(&self) -> usize {
        self.max_scans
    }

    /// Set maximum scans for which the jpeg decoder should
    /// not exceed when reconstructing images.
    pub fn jpeg_set_max_scans(mut self, max_scans: usize) -> Self {
        self.max_scans = max_scans;
        self
    }
    /// Get expected output colorspace set by the user for which the image
    /// is expected to be reconstructed into.
    ///
    /// This may be different from the
    pub const fn jpeg_get_out_colorspace(&self) -> ColorSpace {
        self.out_colorspace
    }
    /// Set expected colorspace for which the jpeg output is expected to be in
    ///
    /// This is mainly provided as is, we do not guarantee the decoder can convert to all colorspaces
    /// and the decoder can change it internally when it sees fit.
    #[must_use]
    pub fn jpeg_set_out_colorspace(mut self, colorspace: ColorSpace) -> Self {
        self.out_colorspace = colorspace;
        self
    }
}

/// Intrinsics support
///
/// These routines are compiled depending
/// on the platform they are used, if compiled for a platform
/// it doesn't support,(e.g avx2 on Arm), it will always return `false`
impl DecoderOptions {
    /// Use SSE 2 code paths where possible
    ///
    /// This checks for existence of SSE2 first and returns
    /// false if it's not present
    #[allow(unreachable_code)]
    pub fn use_sse2(&self) -> bool {
        let opt = self.flags.zune_use_sse2 | self.flags.zune_use_unsafe;
        // options says no
        if !opt {
            return false;
        }

        #[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
        {
            // where we can do runtime check if feature is present
            #[cfg(feature = "std")]
            {
                if is_x86_feature_detected!("sse2") {
                    return true;
                }
            }
            // where we can't do runtime check if feature is present
            // check if the compile feature had it enabled
            #[cfg(all(not(feature = "std"), target_feature = "sse2"))]
            {
                return true;
            }
        }
        // everything failed return false
        false
    }

    /// Use SSE 3 paths where possible
    ///
    ///
    /// This also checks for SSE3 support and returns false if
    /// it's not present
    #[allow(unreachable_code)]
    pub fn use_sse3(&self) -> bool {
        let opt = self.flags.zune_use_sse3 | self.flags.zune_use_unsafe;
        // options says no
        if !opt {
            return false;
        }

        #[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
        {
            // where we can do runtime check if feature is present
            #[cfg(feature = "std")]
            {
                if is_x86_feature_detected!("sse3") {
                    return true;
                }
            }
            // where we can't do runtime check if feature is present
            // check if the compile feature had it enabled
            #[cfg(all(not(feature = "std"), target_feature = "sse3"))]
            {
                return true;
            }
        }
        // everything failed return false
        false
    }

    /// Use SSE4 paths where possible
    ///
    /// This also checks for sse 4.1 support and returns false if it
    /// is not present
    #[allow(unreachable_code)]
    pub fn use_sse41(&self) -> bool {
        let opt = self.flags.zune_use_sse41 | self.flags.zune_use_unsafe;
        // options says no
        if !opt {
            return false;
        }

        #[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
        {
            // where we can do runtime check if feature is present
            #[cfg(feature = "std")]
            {
                if is_x86_feature_detected!("sse4.1") {
                    return true;
                }
            }
            // where we can't do runtime check if feature is present
            // check if the compile feature had it enabled
            #[cfg(all(not(feature = "std"), target_feature = "sse4.1"))]
            {
                return true;
            }
        }
        // everything failed return false
        false
    }

    /// Use AVX paths where possible
    ///
    /// This also checks for AVX support and returns false if it's
    /// not present
    #[allow(unreachable_code)]
    pub fn use_avx(&self) -> bool {
        let opt = self.flags.zune_use_avx | self.flags.zune_use_unsafe;
        // options says no
        if !opt {
            return false;
        }

        #[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
        {
            // where we can do runtime check if feature is present
            #[cfg(feature = "std")]
            {
                if is_x86_feature_detected!("avx") {
                    return true;
                }
            }
            // where we can't do runitme check if feature is present
            // check if the compile feature had it enabled
            #[cfg(all(not(feature = "std"), target_feature = "avx"))]
            {
                return true;
            }
        }
        // everything failed return false
        false
    }

    /// Use avx2 paths where possible
    ///
    /// This also checks for AVX2 support and returns false if it's not
    /// present
    #[allow(unreachable_code)]
    pub fn use_avx2(&self) -> bool {
        let opt = self.flags.zune_use_avx2 | self.flags.zune_use_unsafe;
        // options says no
        if !opt {
            return false;
        }

        #[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
        {
            // where we can do runtime check if feature is present
            #[cfg(feature = "std")]
            {
                if is_x86_feature_detected!("avx2") {
                    return true;
                }
            }
            // where we can't do runitme check if feature is present
            // check if the compile feature had it enabled
            #[cfg(all(not(feature = "std"), target_feature = "avx2"))]
            {
                return true;
            }
        }
        // everything failed return false
        false
    }

    #[allow(unreachable_code)]
    pub fn use_neon(&self) -> bool {
        let opt = self.flags.zune_use_neon | self.flags.zune_use_unsafe;
        // options says no
        if !opt {
            return false;
        }

        #[cfg(target_arch = "aarch64")]
        {
            // aarch64 implies neon on a compliant cpu
            // but for real prod should do something better here
            return true;
        }
        // everything failed return false
        false
    }
}

/// JPEG_XL specific options
impl DecoderOptions {
    /// Return whether `zune-image` should decode animated images or
    /// whether we should just decode the first frame only
    pub const fn jxl_decode_animated(&self) -> bool {
        self.flags.jxl_decode_animated
    }
    /// Set  whether `zune-image` should decode animated images or
    /// whether we should just decode the first frame only
    pub const fn jxl_set_decode_animated(mut self, yes: bool) -> Self {
        self.flags.jxl_decode_animated = yes;
        self
    }
}
impl Default for DecoderOptions {
    fn default() -> Self {
        Self {
            out_colorspace: ColorSpace::RGB,
            max_width:      1 << 14,
            max_height:     1 << 14,
            max_scans:      100,
            deflate_limit:  1 << 30,
            flags:          decoder_strict_mode(),
            endianness:     ByteEndian::BE
        }
    }
}