zenjxl_decoder/api/options.rs
1// Copyright (c) the JPEG XL Project Authors. All rights reserved.
2//
3// Use of this source code is governed by a BSD-style
4// license that can be found in the LICENSE file.
5
6use crate::api::JxlCms;
7
8use std::sync::Arc;
9
10/// Security limits for the JXL decoder to prevent resource exhaustion attacks.
11///
12/// These limits protect against "JXL bombs" - maliciously crafted files designed
13/// to exhaust memory or CPU. All limits are optional; `None` means use the default.
14///
15/// # Example
16/// ```
17/// use zenjxl_decoder::api::JxlDecoderLimits;
18///
19/// // Use restrictive preset for untrusted input
20/// let limits = JxlDecoderLimits::restrictive();
21///
22/// // Or use defaults for normal operation
23/// let defaults = JxlDecoderLimits::default();
24///
25/// // Or unlimited for trusted input (use with caution)
26/// let unlimited = JxlDecoderLimits::unlimited();
27/// ```
28#[derive(Debug, Clone)]
29#[non_exhaustive]
30pub struct JxlDecoderLimits {
31 /// Maximum total pixels (width × height). Default: 2^30 (~1 billion).
32 /// This is checked early during header parsing.
33 pub max_pixels: Option<usize>,
34
35 /// Maximum number of extra channels (alpha, depth, etc.). Default: 256.
36 /// Each extra channel requires memory proportional to image size.
37 pub max_extra_channels: Option<usize>,
38
39 /// Maximum ICC profile size in bytes. Default: 2^28 (256 MB).
40 pub max_icc_size: Option<usize>,
41
42 /// Maximum modular tree size (number of nodes). Default: 2^22.
43 /// Limits memory and CPU for tree-based entropy coding.
44 pub max_tree_size: Option<usize>,
45
46 /// Maximum number of patches. Default: derived from image size.
47 /// Set to limit patch-based attacks.
48 pub max_patches: Option<usize>,
49
50 /// Maximum number of spline control points. Default: 2^20.
51 pub max_spline_points: Option<u32>,
52
53 /// Maximum number of reference frames stored. Default: 4.
54 /// Each reference frame consumes memory equal to the image size.
55 pub max_reference_frames: Option<usize>,
56
57 /// Maximum total memory budget in bytes. Default: None (unlimited).
58 /// When set, the decoder tracks allocations and fails if budget exceeded.
59 /// This provides defense-in-depth against memory exhaustion attacks.
60 pub max_memory_bytes: Option<u64>,
61}
62
63impl Default for JxlDecoderLimits {
64 fn default() -> Self {
65 Self {
66 max_pixels: Some(1 << 28), // ~256 megapixels
67 max_extra_channels: Some(256), // 256 extra channels
68 max_icc_size: Some(1 << 28), // 256 MB
69 max_tree_size: Some(1 << 22), // 4M nodes
70 max_patches: None, // Use image-size-based default
71 max_spline_points: Some(1 << 20), // 1M points
72 max_reference_frames: Some(4), // 4 reference frames
73 max_memory_bytes: None, // No overall memory limit by default
74 }
75 }
76}
77
78impl JxlDecoderLimits {
79 /// Returns limits with no restrictions (all None).
80 /// Use with caution - only for trusted input.
81 pub fn unlimited() -> Self {
82 Self {
83 max_pixels: None,
84 max_extra_channels: None,
85 max_icc_size: None,
86 max_tree_size: None,
87 max_patches: None,
88 max_spline_points: None,
89 max_reference_frames: None,
90 max_memory_bytes: None,
91 }
92 }
93
94 /// Returns restrictive limits suitable for untrusted web content.
95 pub fn restrictive() -> Self {
96 Self {
97 max_pixels: Some(100_000_000), // 100 megapixels
98 max_extra_channels: Some(16), // 16 extra channels
99 max_icc_size: Some(1 << 20), // 1 MB
100 max_tree_size: Some(1 << 20), // 1M nodes
101 max_patches: Some(1 << 16), // 64K patches
102 max_spline_points: Some(1 << 16), // 64K points
103 max_reference_frames: Some(2), // 2 reference frames
104 max_memory_bytes: Some(1 << 30), // 1 GB total memory
105 }
106 }
107}
108
109pub enum JxlProgressiveMode {
110 /// Renders all pixels in every call to Process.
111 Eager,
112 /// Renders pixels once passes are completed.
113 Pass,
114 /// Renders pixels only once the final frame is ready.
115 FullFrame,
116}
117
118#[non_exhaustive]
119pub struct JxlDecoderOptions {
120 pub adjust_orientation: bool,
121 pub render_spot_colors: bool,
122 pub coalescing: bool,
123 pub desired_intensity_target: Option<f32>,
124 pub skip_preview: bool,
125 pub progressive_mode: JxlProgressiveMode,
126 pub cms: Option<Box<dyn JxlCms>>,
127 /// Use high precision mode for decoding.
128 /// When false (default), uses lower precision settings that match libjxl's default.
129 /// When true, uses higher precision at the cost of performance.
130 ///
131 /// This affects multiple decoder decisions including spline rendering precision
132 /// and potentially intermediate buffer storage (e.g., using f32 vs f16).
133 pub high_precision: bool,
134 /// If true, multiply RGB by alpha before writing to output buffer.
135 /// This produces premultiplied alpha output, which is useful for compositing.
136 /// Default: false (output straight alpha)
137 pub premultiply_output: bool,
138 /// Security limits to prevent resource exhaustion attacks.
139 /// Use `JxlDecoderLimits::restrictive()` for untrusted input.
140 pub limits: JxlDecoderLimits,
141 /// Cooperative cancellation / timeout handle.
142 /// Default: `Arc::new(enough::Unstoppable)` (no cancellation).
143 pub stop: Arc<dyn enough::Stop>,
144 /// Enable parallel decoding and rendering using rayon.
145 ///
146 /// When `true` (the default when the `threads` feature is enabled),
147 /// group decoding and rendering are parallelized across rayon's global
148 /// thread pool. Control thread count via `RAYON_NUM_THREADS` or
149 /// `rayon::ThreadPoolBuilder::build_global()`.
150 ///
151 /// When `false`, all decoding is single-threaded.
152 pub parallel: bool,
153}
154
155impl Default for JxlDecoderOptions {
156 fn default() -> Self {
157 Self {
158 adjust_orientation: true,
159 render_spot_colors: true,
160 coalescing: true,
161 skip_preview: true,
162 desired_intensity_target: None,
163 progressive_mode: JxlProgressiveMode::Pass,
164 cms: None,
165 high_precision: false,
166 premultiply_output: false,
167 limits: JxlDecoderLimits::default(),
168 stop: Arc::new(enough::Unstoppable),
169 parallel: cfg!(feature = "threads"),
170 }
171 }
172}