1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
/*
 * Copyright (c) 2023.
 *
 * This software is free software;
 *
 * You can redistribute it or modify it under terms of the MIT, Apache License or Zlib license
 */

use crate::bit_depth::BitDepth;
use crate::colorspace::ColorSpace;

/// Encoder options that are flags
#[derive(Copy, Debug, Clone, Default)]
struct EncoderFlags {
    /// Whether JPEG images should be encoded as progressive images
    jpeg_encode_progressive: bool,
    /// Whether JPEG images should use optimized huffman tables
    jpeg_optimize_huffman:   bool,
    /// Whether to not preserve metadata across image transformations
    image_strip_metadata:    bool
}

/// Options shared by some of the encoders in
/// the `zune-` family of image crates
#[derive(Debug, Copy, Clone)]
pub struct EncoderOptions {
    width:       usize,
    height:      usize,
    colorspace:  ColorSpace,
    quality:     u8,
    depth:       BitDepth,
    num_threads: u8,
    effort:      u8,
    flags:       EncoderFlags
}

impl Default for EncoderOptions {
    fn default() -> Self {
        Self {
            width:       0,
            height:      0,
            colorspace:  ColorSpace::RGB,
            quality:     80,
            depth:       BitDepth::Eight,
            num_threads: 4,
            effort:      4,
            flags:       EncoderFlags::default()
        }
    }
}

impl EncoderOptions {
    ///  Create  new encode options
    ///
    /// # Arguments
    ///  
    /// * `width`: Image width
    /// * `height`: Image height
    /// * `colorspace`:  Image colorspaces
    /// * `depth`: Image depth
    ///
    /// returns: EncoderOptions
    ///
    pub fn new(
        width: usize, height: usize, colorspace: ColorSpace, depth: BitDepth
    ) -> EncoderOptions {
        EncoderOptions {
            width,
            height,
            colorspace,
            depth,
            ..Default::default()
        }
    }
    /// Get the width for which the image will be encoded in
    pub const fn get_width(&self) -> usize {
        self.width
    }

    /// Get height for which the image will be encoded in
    ///
    /// returns: usize
    ///
    /// # Panics
    /// If height is zero
    pub fn get_height(&self) -> usize {
        assert_ne!(self.height, 0);
        self.height
    }
    /// Get the depth for which the image will be encoded in
    pub const fn get_depth(&self) -> BitDepth {
        self.depth
    }
    /// Get the quality for which the image will be encoded with
    ///
    ///  # Lossy
    /// - Higher quality means some images take longer to write and
    /// are big but they look good
    ///
    /// - Lower quality means small images and low quality.
    ///
    /// # Lossless
    /// - High quality indicates more time is spent in making the file
    /// smaller
    ///
    /// - Low quality indicates less time is spent in making the file bigger
    pub const fn get_quality(&self) -> u8 {
        self.quality
    }
    /// Get the colorspace for which the image will be encoded in
    pub const fn get_colorspace(&self) -> ColorSpace {
        self.colorspace
    }
    pub const fn get_effort(&self) -> u8 {
        self.effort
    }

    /// Set width for the image to be encoded
    pub fn set_width(mut self, width: usize) -> Self {
        self.width = width;
        self
    }

    /// Set height for the image to be encoded
    pub fn set_height(mut self, height: usize) -> Self {
        self.height = height;
        self
    }
    /// Set depth for the image to be encoded
    pub fn set_depth(mut self, depth: BitDepth) -> Self {
        self.depth = depth;
        self
    }
    /// Set quality of the image to be encoded
    ///
    /// Quality is clamped from 0..100
    ///
    /// Quality means different options depending on the encoder, see
    /// [get_quality](Self::get_quality)
    pub fn set_quality(mut self, quality: u8) -> Self {
        self.quality = quality.clamp(0, 100);
        self
    }
    /// Set colorspace for the image to be encoded
    pub fn set_colorspace(mut self, colorspace: ColorSpace) -> Self {
        self.colorspace = colorspace;
        self
    }
    /// Set the number of threads allowed for multithreaded encoding
    /// where supported
    ///
    /// Zero means use a single thread
    pub fn set_num_threads(mut self, threads: u8) -> Self {
        self.num_threads = threads;

        self
    }
    pub fn set_effort(mut self, effort: u8) -> Self {
        self.effort = effort;
        self
    }

    /// Return number of threads configured for multithreading
    /// where possible
    ///
    /// This is used for multi-threaded encoders,
    /// currently only jpeg-xl
    pub const fn get_num_threads(&self) -> u8 {
        self.num_threads
    }

    /// Set whether the encoder should remove metadata from the image
    ///
    /// When set to `true`, supported encoders will strip away metadata
    /// from the resulting image. If set to false, where supported, encoders
    /// will not remove metadata from images
    pub fn set_strip_metadata(mut self, yes: bool) -> Self {
        self.flags.image_strip_metadata = yes;
        self
    }
    /// Whether or not the encoder should remove metadata from the image
    ///
    /// The default value is false, and encoders that respect this try to preserve as much
    /// data as possible from one image to another
    pub const fn strip_metadata(&self) -> bool {
        !self.flags.image_strip_metadata
    }
}

/// JPEG options
impl EncoderOptions {
    /// Whether the jpeg encoder should encode the image in progressive mode
    ///
    /// Default is `false`.
    ///
    /// This may be used to create slightly smaller images at the cost of more processing
    /// time
    pub const fn jpeg_encode_progressive(&self) -> bool {
        self.flags.jpeg_encode_progressive
    }

    /// Whether the jpeg encoder should optimize huffman tables to create smaller files
    /// at the cost of processing time
    ///
    /// Default is `false`.
    pub const fn jpeg_optimized_huffman_tables(&self) -> bool {
        self.flags.jpeg_optimize_huffman
    }

    /// Set whether the jpeg encoder should encode the imagei in progressive mode
    ///
    /// Default is `false`
    pub fn set_jpeg_encode_progressive(mut self, yes: bool) -> Self {
        self.flags.jpeg_optimize_huffman = yes;
        self
    }
}