makepad_zune_core/options/
decoder.rs

1/*
2 * Copyright (c) 2023.
3 *
4 * This software is free software;
5 *
6 * You can redistribute it or modify it under terms of the MIT, Apache License or Zlib license
7 */
8
9//! Global Decoder options
10#![allow(clippy::zero_prefixed_literal)]
11
12use bitflags::bitflags;
13
14use crate::bit_depth::ByteEndian;
15use crate::colorspace::ColorSpace;
16
17fn decoder_strict_mode() -> DecoderFlags {
18    let mut flags = DecoderFlags::empty();
19
20    flags.set(DecoderFlags::INFLATE_CONFIRM_ADLER, true);
21    flags.set(DecoderFlags::PNG_CONFIRM_CRC, true);
22    flags.set(DecoderFlags::JPG_ERROR_ON_NON_CONFORMANCE, true);
23
24    flags.set(DecoderFlags::ZUNE_USE_UNSAFE, true);
25    flags.set(DecoderFlags::ZUNE_USE_NEON, true);
26    flags.set(DecoderFlags::ZUNE_USE_AVX, true);
27    flags.set(DecoderFlags::ZUNE_USE_AVX2, true);
28    flags.set(DecoderFlags::ZUNE_USE_SSE2, true);
29    flags.set(DecoderFlags::ZUNE_USE_SSE3, true);
30    flags.set(DecoderFlags::ZUNE_USE_SSE41, true);
31    flags.set(DecoderFlags::PNG_ADD_ALPHA_CHANNEL, false);
32
33    flags
34}
35
36/// Fast decoder options
37///
38/// Enables all intrinsics + unsafe routines
39///
40/// Disables png adler and crc checking.
41fn fast_options() -> DecoderFlags {
42    let mut flags = DecoderFlags::empty();
43
44    flags.set(DecoderFlags::INFLATE_CONFIRM_ADLER, false);
45    flags.set(DecoderFlags::PNG_CONFIRM_CRC, false);
46    flags.set(DecoderFlags::JPG_ERROR_ON_NON_CONFORMANCE, false);
47
48    flags.set(DecoderFlags::ZUNE_USE_UNSAFE, true);
49    flags.set(DecoderFlags::ZUNE_USE_NEON, true);
50    flags.set(DecoderFlags::ZUNE_USE_AVX, true);
51    flags.set(DecoderFlags::ZUNE_USE_AVX2, true);
52    flags.set(DecoderFlags::ZUNE_USE_SSE2, true);
53    flags.set(DecoderFlags::ZUNE_USE_SSE3, true);
54    flags.set(DecoderFlags::ZUNE_USE_SSE41, true);
55    flags.set(DecoderFlags::PNG_ADD_ALPHA_CHANNEL, false);
56
57    flags
58}
59
60/// Command line options error resilient and fast
61///
62/// Features
63/// - Ignore CRC and Adler in png
64/// - Do not error out on non-conformance in jpg
65/// - Use unsafe paths
66fn cmd_options() -> DecoderFlags {
67    let mut flags = DecoderFlags::empty();
68
69    flags.set(DecoderFlags::INFLATE_CONFIRM_ADLER, false);
70    flags.set(DecoderFlags::PNG_CONFIRM_CRC, false);
71    flags.set(DecoderFlags::JPG_ERROR_ON_NON_CONFORMANCE, false);
72
73    flags.set(DecoderFlags::ZUNE_USE_UNSAFE, true);
74    flags.set(DecoderFlags::ZUNE_USE_NEON, true);
75    flags.set(DecoderFlags::ZUNE_USE_AVX, true);
76    flags.set(DecoderFlags::ZUNE_USE_AVX2, true);
77    flags.set(DecoderFlags::ZUNE_USE_SSE2, true);
78    flags.set(DecoderFlags::ZUNE_USE_SSE3, true);
79    flags.set(DecoderFlags::ZUNE_USE_SSE41, true);
80    flags.set(DecoderFlags::PNG_ADD_ALPHA_CHANNEL, false);
81
82    flags
83}
84
85bitflags! {
86    /// Decoder options that are flags
87    ///
88    /// NOTE: When you extend this, add true or false to
89    /// all options above that return a `DecoderFlag`
90    #[derive(Copy,Debug,Clone)]
91    pub struct  DecoderFlags:u64{
92        /// Whether the decoder should confirm and report adler mismatch
93        const INFLATE_CONFIRM_ADLER         = 1<<01;
94        /// Whether the PNG decoder should confirm crc
95        const PNG_CONFIRM_CRC               = 1<<02;
96        /// Whether the png decoder should error out on image non-conformance
97        const JPG_ERROR_ON_NON_CONFORMANCE  = 1<<03;
98        /// Whether the decoder should use unsafe  platform specific intrinsics
99        ///
100        /// This will also shut down platform specific intrinsics `(ZUNE_USE_{EXT})` value
101        const ZUNE_USE_UNSAFE               = 0b0000_0000_0000_0000_0000_0000_0000_1000;
102        /// Whether we should use SSE2.
103        ///
104        /// This should be enabled for all x64 platforms but can be turned off if
105        /// `ZUNE_USE_UNSAFE` is false
106        const ZUNE_USE_SSE2                 =  1<<05;
107        /// Whether we should use SSE3 instructions where possible.
108        const ZUNE_USE_SSE3                 =  1<<06;
109        /// Whether we should use sse4.1 instructions where possible.
110        const ZUNE_USE_SSE41                =  1<<07;
111        /// Whether we should use avx instructions where possible.
112        const ZUNE_USE_AVX                  =  1<<08;
113        /// Whether we should use avx2 instructions where possible.
114        const ZUNE_USE_AVX2                 =  1<<09;
115        /// Whether the png decoder should add alpha channel where possible.
116        const PNG_ADD_ALPHA_CHANNEL         =  1<<10;
117        /// Whether we should use neon instructions where possible.
118        const ZUNE_USE_NEON                 =  1<<11;
119    }
120}
121
122/// Decoder options
123///
124/// Not all options are respected by decoders all decoders
125#[derive(Debug, Copy, Clone)]
126pub struct DecoderOptions {
127    /// Maximum width for which decoders will
128    /// not try to decode images larger than
129    /// the specified width.
130    ///
131    /// - Default value: 16384
132    /// - Respected by: `all decoders`
133    max_width:      usize,
134    /// Maximum height for which decoders will not
135    /// try to decode images larger than the
136    /// specified height
137    ///
138    /// - Default value: 16384
139    /// - Respected by: `all decoders`
140    max_height:     usize,
141    /// Output colorspace
142    ///
143    /// The jpeg decoder allows conversion to a separate colorspace
144    /// than the input.
145    ///
146    /// I.e you can convert a RGB jpeg image to grayscale without
147    /// first decoding it to RGB to get
148    ///
149    /// - Default value: `ColorSpace::RGB`
150    /// - Respected by: `jpeg`
151    out_colorspace: ColorSpace,
152
153    /// Maximum number of scans allowed
154    /// for progressive jpeg images
155    ///
156    /// Progressive jpegs have scans
157    ///
158    /// - Default value:100
159    /// - Respected by: `jpeg`
160    max_scans:     usize,
161    /// Maximum size for deflate.
162    /// Respected by all decoders that use inflate/deflate
163    deflate_limit: usize,
164    /// Boolean flags that influence decoding
165    flags:         DecoderFlags,
166    /// The byte endian of the returned bytes will be stored in
167    /// in case a single pixel spans more than a byte
168    endianness:    ByteEndian
169}
170
171/// Initializers
172impl DecoderOptions {
173    /// Create the decoder with options  setting most configurable
174    /// options to be their safe counterparts
175    ///
176    /// This is the same as `default` option as default initializes
177    /// options to the  safe variant.
178    ///
179    /// Note, decoders running on this will be slower as it disables
180    /// platform specific intrinsics
181    pub fn new_safe() -> DecoderOptions {
182        DecoderOptions::default()
183    }
184
185    /// Create the decoder with options setting the configurable options
186    /// to the fast  counterparts
187    ///
188    /// This enables platform specific code paths and enable use of unsafe
189    pub fn new_fast() -> DecoderOptions {
190        let flag = fast_options();
191        DecoderOptions::default().set_decoder_flags(flag)
192    }
193
194    /// Create the decoder options with the following characteristics
195    ///
196    /// - Use unsafe paths.
197    /// - Ignore error checksuming, e.g in png we do not confirm adler and crc in this mode
198    /// - Enable fast intrinsics paths
199    pub fn new_cmd() -> DecoderOptions {
200        let flag = cmd_options();
201        DecoderOptions::default().set_decoder_flags(flag)
202    }
203}
204
205/// Global options respected by all decoders
206impl DecoderOptions {
207    /// Get maximum width configured for which the decoder
208    /// should not try to decode images greater than this width
209    pub const fn get_max_width(&self) -> usize {
210        self.max_width
211    }
212
213    /// Get maximum height configured for which the decoder should
214    /// not try to decode images greater than this height
215    pub const fn get_max_height(&self) -> usize {
216        self.max_height
217    }
218
219    /// Return true whether the decoder should be in strict mode
220    /// And reject most errors
221    pub fn get_strict_mode(&self) -> bool {
222        let flags = DecoderFlags::JPG_ERROR_ON_NON_CONFORMANCE
223            | DecoderFlags::PNG_CONFIRM_CRC
224            | DecoderFlags::INFLATE_CONFIRM_ADLER;
225
226        self.flags.contains(flags)
227    }
228    /// Return true if the decoder should use unsafe
229    /// routines where possible
230    pub const fn get_use_unsafe(&self) -> bool {
231        self.flags.contains(DecoderFlags::ZUNE_USE_UNSAFE)
232    }
233
234    /// Set maximum width for which the decoder should not try
235    /// decoding images greater than that width
236    ///
237    /// # Arguments
238    ///
239    /// * `width`:  The maximum width allowed
240    ///
241    /// returns: DecoderOptions
242    pub fn set_max_width(mut self, width: usize) -> Self {
243        self.max_width = width;
244        self
245    }
246
247    /// Set maximum height for which the decoder should not try
248    /// decoding images greater than that height
249    /// # Arguments
250    ///
251    /// * `height`: The maximum height allowed
252    ///
253    /// returns: DecoderOptions
254    ///
255    pub fn set_max_height(mut self, height: usize) -> Self {
256        self.max_height = height;
257        self
258    }
259
260    /// Whether the routines can use unsafe platform specific
261    /// intrinsics when necessary
262    ///
263    /// Platform intrinsics are implemented for operations which
264    /// the compiler can't auto-vectorize, or we can do a marginably
265    /// better job at it
266    ///
267    /// All decoders with unsafe routines respect it.
268    ///
269    /// Treat this with caution, disabling it will cause slowdowns but
270    /// it's provided for mainly for debugging use.
271    ///
272    /// - Respected by: `png` and `jpeg`(decoders with unsafe routines)
273    pub fn set_use_unsafe(mut self, yes: bool) -> Self {
274        // first clear the flag
275        self.flags.set(DecoderFlags::ZUNE_USE_UNSAFE, yes);
276        self
277    }
278
279    fn set_decoder_flags(mut self, flags: DecoderFlags) -> Self {
280        self.flags = flags;
281        self
282    }
283    /// Set whether the decoder should be in standards conforming/
284    /// strict mode
285    ///
286    /// This reduces the error tolerance level for the decoders and invalid
287    /// samples will be rejected by the decoder
288    ///
289    /// # Arguments
290    ///
291    /// * `yes`:
292    ///
293    /// returns: DecoderOptions
294    ///
295    pub fn set_strict_mode(mut self, yes: bool) -> Self {
296        let flags = DecoderFlags::JPG_ERROR_ON_NON_CONFORMANCE
297            | DecoderFlags::PNG_CONFIRM_CRC
298            | DecoderFlags::INFLATE_CONFIRM_ADLER;
299
300        self.flags.set(flags, yes);
301        self
302    }
303
304    /// Set the byte endian for which raw samples will be stored in
305    /// in case a single pixel sample spans more than a byte.
306    ///
307    /// The default is usually native endian hence big endian values
308    /// will be converted to little endian on little endian systems,
309    ///
310    /// and little endian values will be converted to big endian on big endian systems
311    ///
312    /// # Arguments
313    ///
314    /// * `endian`: The endianness to which to set the bytes to
315    ///
316    /// returns: DecoderOptions
317    pub fn set_byte_endian(mut self, endian: ByteEndian) -> Self {
318        self.endianness = endian;
319        self
320    }
321
322    /// Get the byte endian for which samples that span more than one byte will
323    /// be treated
324    pub const fn get_byte_endian(&self) -> ByteEndian {
325        self.endianness
326    }
327}
328
329/// PNG specific options
330impl DecoderOptions {
331    /// Whether the inflate decoder should confirm
332    /// adler checksums
333    pub const fn inflate_get_confirm_adler(&self) -> bool {
334        self.flags.contains(DecoderFlags::INFLATE_CONFIRM_ADLER)
335    }
336    /// Set whether the inflate decoder should confirm
337    /// adler checksums
338    pub fn inflate_set_confirm_adler(mut self, yes: bool) -> Self {
339        self.flags.set(DecoderFlags::INFLATE_CONFIRM_ADLER, yes);
340        self
341    }
342    /// Get default inflate limit for which the decoder
343    /// will not try to decompress further
344    pub const fn inflate_get_limit(&self) -> usize {
345        self.deflate_limit
346    }
347    /// Set the default inflate limit for which decompressors
348    /// relying on inflate won't surpass this limit
349    #[must_use]
350    pub fn inflate_set_limit(mut self, limit: usize) -> Self {
351        self.deflate_limit = limit;
352        self
353    }
354    /// Whether the inflate decoder should confirm
355    /// crc 32 checksums
356    pub const fn png_get_confirm_crc(&self) -> bool {
357        self.flags.contains(DecoderFlags::PNG_CONFIRM_CRC)
358    }
359    /// Set whether the png decoder should confirm
360    /// CRC 32 checksums
361    #[must_use]
362    pub fn png_set_confirm_crc(mut self, yes: bool) -> Self {
363        self.flags.set(DecoderFlags::PNG_CONFIRM_CRC, yes);
364        self
365    }
366    /// Set whether the png decoder should add an alpha channel to
367    /// images where possible.
368    ///
369    /// For Luma images, it converts it to Luma+Alpha
370    ///
371    /// For RGB images it converts it to RGB+Alpha
372    pub fn png_set_add_alpha_channel(mut self, yes: bool) -> Self {
373        self.flags.set(DecoderFlags::PNG_ADD_ALPHA_CHANNEL, yes);
374        self
375    }
376    /// Return true whether the png decoder should add an alpha
377    /// channel to images where possible
378    pub const fn png_get_add_alpha_channel(&self) -> bool {
379        self.flags.contains(DecoderFlags::PNG_ADD_ALPHA_CHANNEL)
380    }
381}
382
383/// JPEG specific options
384impl DecoderOptions {
385    /// Get maximum scans for which the jpeg decoder
386    /// should not go above for progressive images
387    pub const fn jpeg_get_max_scans(&self) -> usize {
388        self.max_scans
389    }
390
391    /// Set maximum scans for which the jpeg decoder should
392    /// not exceed when reconstructing images.
393    pub fn jpeg_set_max_scans(mut self, max_scans: usize) -> Self {
394        self.max_scans = max_scans;
395        self
396    }
397    /// Get expected output colorspace set by the user for which the image
398    /// is expected to be reconstructed into.
399    ///
400    /// This may be different from the
401    pub const fn jpeg_get_out_colorspace(&self) -> ColorSpace {
402        self.out_colorspace
403    }
404    /// Set expected colorspace for which the jpeg output is expected to be in
405    ///
406    /// This is mainly provided as is, we do not guarantee the decoder can convert to all colorspaces
407    /// and the decoder can change it internally when it sees fit.
408    #[must_use]
409    pub fn jpeg_set_out_colorspace(mut self, colorspace: ColorSpace) -> Self {
410        self.out_colorspace = colorspace;
411        self
412    }
413}
414
415/// Intrinsics support
416///
417/// These routines are compiled depending
418/// on the platform they are used, if compiled for a platform
419/// it doesn't support,(e.g avx2 on Arm), it will always return `false`
420impl DecoderOptions {
421    /// Use SSE 2 code paths where possible
422    ///
423    /// This checks for existence of SSE2 first and returns
424    /// false if it's not present
425    #[allow(unreachable_code)]
426    pub fn use_sse2(&self) -> bool {
427        let opt = self
428            .flags
429            .contains(DecoderFlags::ZUNE_USE_SSE2 | DecoderFlags::ZUNE_USE_UNSAFE);
430        // options says no
431        if !opt {
432            return false;
433        }
434
435        #[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
436        {
437            // where we can do runtime check if feature is present
438            #[cfg(feature = "std")]
439            {
440                if is_x86_feature_detected!("sse2") {
441                    return true;
442                }
443            }
444            // where we can't do runtime check if feature is present
445            // check if the compile feature had it enabled
446            #[cfg(all(not(feature = "std"), target_feature = "sse2"))]
447            {
448                return true;
449            }
450        }
451        // everything failed return false
452        false
453    }
454
455    /// Use SSE 3 paths where possible
456    ///
457    ///
458    /// This also checks for SSE3 support and returns false if
459    /// it's not present
460    #[allow(unreachable_code)]
461    pub fn use_sse3(&self) -> bool {
462        let opt = self
463            .flags
464            .contains(DecoderFlags::ZUNE_USE_SSE3 | DecoderFlags::ZUNE_USE_UNSAFE);
465        // options says no
466        if !opt {
467            return false;
468        }
469
470        #[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
471        {
472            // where we can do runtime check if feature is present
473            #[cfg(feature = "std")]
474            {
475                if is_x86_feature_detected!("sse3") {
476                    return true;
477                }
478            }
479            // where we can't do runtime check if feature is present
480            // check if the compile feature had it enabled
481            #[cfg(all(not(feature = "std"), target_feature = "sse3"))]
482            {
483                return true;
484            }
485        }
486        // everything failed return false
487        false
488    }
489
490    /// Use SSE4 paths where possible
491    ///
492    /// This also checks for sse 4.1 support and returns false if it
493    /// is not present
494    #[allow(unreachable_code)]
495    pub fn use_sse41(&self) -> bool {
496        let opt = self
497            .flags
498            .contains(DecoderFlags::ZUNE_USE_SSE41 | DecoderFlags::ZUNE_USE_UNSAFE);
499        // options says no
500        if !opt {
501            return false;
502        }
503
504        #[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
505        {
506            // where we can do runtime check if feature is present
507            #[cfg(feature = "std")]
508            {
509                if is_x86_feature_detected!("sse4.1") {
510                    return true;
511                }
512            }
513            // where we can't do runtime check if feature is present
514            // check if the compile feature had it enabled
515            #[cfg(all(not(feature = "std"), target_feature = "sse4.1"))]
516            {
517                return true;
518            }
519        }
520        // everything failed return false
521        false
522    }
523
524    /// Use AVX paths where possible
525    ///
526    /// This also checks for AVX support and returns false if it's
527    /// not present
528    #[allow(unreachable_code)]
529    pub fn use_avx(&self) -> bool {
530        let opt = self
531            .flags
532            .contains(DecoderFlags::ZUNE_USE_AVX | DecoderFlags::ZUNE_USE_UNSAFE);
533        // options says no
534        if !opt {
535            return false;
536        }
537
538        #[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
539        {
540            // where we can do runtime check if feature is present
541            #[cfg(feature = "std")]
542            {
543                if is_x86_feature_detected!("avx") {
544                    return true;
545                }
546            }
547            // where we can't do runitme check if feature is present
548            // check if the compile feature had it enabled
549            #[cfg(all(not(feature = "std"), target_feature = "avx"))]
550            {
551                return true;
552            }
553        }
554        // everything failed return false
555        false
556    }
557
558    /// Use avx2 paths where possible
559    ///
560    /// This also checks for AVX2 support and returns false if it's not
561    /// present
562    #[allow(unreachable_code)]
563    pub fn use_avx2(&self) -> bool {
564        let opt = self
565            .flags
566            .contains(DecoderFlags::ZUNE_USE_AVX2 | DecoderFlags::ZUNE_USE_UNSAFE);
567        // options says no
568        if !opt {
569            return false;
570        }
571
572        #[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
573        {
574            // where we can do runtime check if feature is present
575            #[cfg(feature = "std")]
576            {
577                if is_x86_feature_detected!("avx2") {
578                    return true;
579                }
580            }
581            // where we can't do runitme check if feature is present
582            // check if the compile feature had it enabled
583            #[cfg(all(not(feature = "std"), target_feature = "avx2"))]
584            {
585                return true;
586            }
587        }
588        // everything failed return false
589        false
590    }
591
592    #[allow(unreachable_code)]
593    pub fn use_neon(&self) -> bool {
594        let opt = self
595            .flags
596            .contains(DecoderFlags::ZUNE_USE_NEON | DecoderFlags::ZUNE_USE_UNSAFE);
597        // options says no
598        if !opt {
599            return false;
600        }
601
602        #[cfg(target_arch = "aarch64")]
603        {
604            // aarch64 implies neon on a compliant cpu
605            // but for real prod should do something better here
606            return true;
607        }
608        // everything failed return false
609        false
610    }
611}
612
613impl Default for DecoderOptions {
614    fn default() -> Self {
615        Self {
616            out_colorspace: ColorSpace::RGB,
617            max_width:      1 << 14,
618            max_height:     1 << 14,
619            max_scans:      100,
620            deflate_limit:  1 << 30,
621            flags:          decoder_strict_mode(),
622            endianness:     ByteEndian::BE
623        }
624    }
625}