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