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}