zune_core/options/encoder.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
9use crate::bit_depth::BitDepth;
10use crate::colorspace::ColorSpace;
11
12/// The type and strength of applied compression.
13///
14/// This is a simple, high-level interface that will automatically choose
15/// the appropriate DEFLATE compression mode and PNG filter.
16///
17/// If you need more control over the encoding parameters,
18/// you can set the [DeflateCompression] and [Filter] manually.
19#[derive(Debug, Clone, Copy, Default)]
20pub enum PngCompression {
21 /// No compression whatsoever. Fastest, but results in large files.
22 NoCompression,
23 /// Extremely fast but light compression.
24 ///
25 /// Note: When used in streaming mode, this compression level can actually result in files
26 /// *larger* than would be produced by `NoCompression` on incompressible data because
27 /// it doesn't do any buffering of the output stream to detect whether the data is being compressed or not.
28 Fastest,
29 /// Extremely fast compression with a decent compression ratio.
30 ///
31 /// Significantly outperforms libpng and other popular encoders by using a [specialized DEFLATE
32 /// implementation tuned for PNG](https://crates.io/crates/fdeflate), while still providing
33 /// better compression ratio than the fastest modes of other encoders.
34 ///
35 /// Like `Compression::Fast` this can currently produce files larger than `NoCompression` in
36 /// streaming mode when given incompressible data. This may change in the future.
37 Fast,
38 /// Balances encoding speed and compression ratio
39 #[default]
40 Balanced,
41 /// Spend much more time to produce a slightly smaller file than with `Balanced`.
42 High
43}
44/// Encoder options that are flags
45#[derive(Copy, Debug, Clone, Default)]
46struct EncoderFlags {
47 /// Whether JPEG images should be encoded as progressive images
48 jpeg_encode_progressive: bool,
49 /// Whether JPEG images should use optimized huffman tables
50 jpeg_optimize_huffman: bool,
51 /// Whether to not preserve metadata across image transformations
52 image_strip_metadata: bool,
53 /// png compression
54 png_compression_level: PngCompression
55}
56
57/// Options shared by some of the encoders in
58/// the `zune-` family of image crates
59#[derive(Debug, Copy, Clone)]
60pub struct EncoderOptions {
61 width: usize,
62 height: usize,
63 colorspace: ColorSpace,
64 quality: u8,
65 depth: BitDepth,
66 num_threads: u8,
67 effort: u8,
68 flags: EncoderFlags
69}
70
71impl Default for EncoderOptions {
72 fn default() -> Self {
73 Self {
74 width: 0,
75 height: 0,
76 colorspace: ColorSpace::RGB,
77 quality: 80,
78 depth: BitDepth::Eight,
79 num_threads: 4,
80 effort: 4,
81 flags: EncoderFlags::default()
82 }
83 }
84}
85
86impl EncoderOptions {
87 /// Create new encode options
88 ///
89 /// # Arguments
90 ///
91 /// * `width`: Image width
92 /// * `height`: Image height
93 /// * `colorspace`: Image colorspaces
94 /// * `depth`: Image depth
95 ///
96 /// returns: EncoderOptions
97 ///
98 pub fn new(
99 width: usize, height: usize, colorspace: ColorSpace, depth: BitDepth
100 ) -> EncoderOptions {
101 EncoderOptions {
102 width,
103 height,
104 colorspace,
105 depth,
106 ..Default::default()
107 }
108 }
109 /// Get the width for which the image will be encoded in
110 pub const fn width(&self) -> usize {
111 self.width
112 }
113
114 /// Get height for which the image will be encoded in
115 ///
116 /// returns: usize
117 ///
118 /// # Panics
119 /// If height is zero
120 pub fn height(&self) -> usize {
121 assert_ne!(self.height, 0);
122 self.height
123 }
124 /// Get the depth for which the image will be encoded in
125 pub const fn depth(&self) -> BitDepth {
126 self.depth
127 }
128 /// Get the quality for which the image will be encoded with
129 ///
130 /// # Lossy
131 /// - Higher quality means some images take longer to write and
132 /// have large file sizes but they look good (closer to lossless)
133 ///
134 /// - Lower quality means small images and low quality.
135 ///
136 /// # Lossless
137 /// - High quality indicates more time is spent in making the file
138 /// smaller
139 ///
140 /// - Low quality indicates less time is spent in making the file bigger
141 pub const fn quality(&self) -> u8 {
142 self.quality
143 }
144 /// Get the colorspace for which the image will be encoded in
145 pub const fn colorspace(&self) -> ColorSpace {
146 self.colorspace
147 }
148 pub const fn effort(&self) -> u8 {
149 self.effort
150 }
151
152 /// Set width for the image to be encoded
153 pub fn set_width(mut self, width: usize) -> Self {
154 self.width = width;
155 self
156 }
157
158 /// Set height for the image to be encoded
159 pub fn set_height(mut self, height: usize) -> Self {
160 self.height = height;
161 self
162 }
163 /// Set depth for the image to be encoded
164 pub fn set_depth(mut self, depth: BitDepth) -> Self {
165 self.depth = depth;
166 self
167 }
168 /// Set quality of the image to be encoded
169 ///
170 /// Quality is clamped from 0..100
171 ///
172 /// Quality means different options depending on the encoder, see
173 /// [get_quality](Self::quality)
174 pub fn set_quality(mut self, quality: u8) -> Self {
175 self.quality = quality.clamp(0, 100);
176 self
177 }
178 /// Set colorspace for the image to be encoded
179 pub fn set_colorspace(mut self, colorspace: ColorSpace) -> Self {
180 self.colorspace = colorspace;
181 self
182 }
183 /// Set the number of threads allowed for multithreaded encoding
184 /// where supported
185 ///
186 /// Zero means use a single thread
187 pub fn set_num_threads(mut self, threads: u8) -> Self {
188 self.num_threads = threads;
189
190 self
191 }
192 pub fn set_effort(mut self, effort: u8) -> Self {
193 self.effort = effort;
194 self
195 }
196
197 /// Return number of threads configured for multithreading
198 /// where possible
199 ///
200 /// This is used for multi-threaded encoders,
201 /// currently only jpeg-xl
202 pub const fn num_threads(&self) -> u8 {
203 self.num_threads
204 }
205
206 /// Set whether the encoder should remove metadata from the image
207 ///
208 /// When set to `true`, supported encoders will strip away metadata
209 /// from the resulting image. If set to false, where supported, encoders
210 /// will not remove metadata from images
211 pub fn set_strip_metadata(mut self, yes: bool) -> Self {
212 self.flags.image_strip_metadata = yes;
213 self
214 }
215 /// Whether or not the encoder should remove metadata from the image
216 ///
217 /// The default value is false, and encoders that respect this try to preserve as much
218 /// data as possible from one image to another
219 pub const fn strip_metadata(&self) -> bool {
220 self.flags.image_strip_metadata
221 }
222}
223
224/// JPEG options
225impl EncoderOptions {
226 /// Whether the jpeg encoder should encode the image in progressive mode
227 ///
228 /// Default is `false`.
229 ///
230 /// This may be used to create slightly smaller images at the cost of more processing
231 /// time
232 pub const fn jpeg_encode_progressive(&self) -> bool {
233 self.flags.jpeg_encode_progressive
234 }
235
236 /// Whether the jpeg encoder should optimize huffman tables to create smaller files
237 /// at the cost of processing time
238 ///
239 /// Default is `false`.
240 pub const fn jpeg_optimized_huffman_tables(&self) -> bool {
241 self.flags.jpeg_optimize_huffman
242 }
243
244 /// Set whether the jpeg encoder should encode the imagei in progressive mode
245 ///
246 /// Default is `false`
247 pub fn set_jpeg_encode_progressive(mut self, yes: bool) -> Self {
248 self.flags.jpeg_optimize_huffman = yes;
249 self
250 }
251}
252
253impl EncoderOptions {
254 /// Get png compression level
255 pub const fn png_compression_level(&self) -> PngCompression {
256 self.flags.png_compression_level
257 }
258 /// Set png compression level
259 pub fn set_png_compression_level(mut self, value: PngCompression) -> Self {
260 self.flags.png_compression_level = value;
261 self
262 }
263}