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
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
//! Traits, types and functions to assist in encoding commonly used _icon formats_.

use crate::{AsSize, Image};
use image::{png::PNGEncoder, bmp::BMPEncoder, ColorType, DynamicImage, GenericImageView};
use std::{io::{self, BufWriter}, path::Path, fs::File};
use resvg::usvg::{Tree, XmlIndent, XmlOptions};
pub use error::EncodingError;

mod error;

const XML_OPTS: XmlOptions = XmlOptions {
    indent: XmlIndent::None,
    attributes_indent: XmlIndent::None,
    use_single_quote: false,
};

const STD_CAPACITY: usize = 7;

/// The `Encode` trait represents a generic icon encoder, providing basic
/// inicialization methods as well as functionality for adding _entries_.
/// 
/// # Example
/// 
/// In this example we'll create a very simple `Encode` implementor whose
/// keys are _positive integers_. First of all, we'll need a `Key` type:
/// 
/// ```rust
/// #[derive(Copy, Clone, Debug, PartialEq, Eq)]
/// pub struct Key(pub u16);
/// 
/// impl AsSize for Key {
///     fn as_size(&self) -> u32 {
///         if self.0 == 0 {
///             256
///         } else {
///             *self.0
///         }
///     }
/// }
/// ```
/// 
/// Note that `Key(0)` represents `Key(256)`. We can then implement our `Icon` type:
/// 
/// ```rust
/// #[derive(Clone)]
/// pub struct Icon {
///     internal: HashMap<u16, DynamicImage>
/// }
/// 
/// impl Encode for Icon {
///     type Key = Key;
/// 
///     fn with_capacity(capacity: usize) -> Self {
///         Self { internal: HashMap::with_capacity(capacity) }
///     }
/// 
///     fn add_entry<F: FnMut(&DynamicImage, u32) -> io::Result<DynamicImage>>(
///         &mut self,
///         filter: F,
///         source: &Image,
///         key: Self::Key,
///     ) -> Result<(), EncodingError<Self::Key>> {
///         let size = key.as_size();
/// 
///         if let Entry::Vacant(entry) = self.internal.entry(size) {
///             entry.insert(source.rasterize(filter, size));
///             Ok(())
///         } else {
///             Err(EncodingError::AlreadyIncluded(key))
///         }
///     }
/// }
/// ```
pub trait Encode: Sized {
    type Key: AsSize + Send + Sync;

    /// Creates a new icon.
    /// 
    /// # Example
    /// 
    /// ```rust
    /// let icon = Icon::new();
    /// ```
    fn new() -> Self {
        Self::with_capacity(STD_CAPACITY)
    }

    /// Constructs a new, empty `IconEncoder` with the specified capacity.
    /// The `capacity` argument designates the number of entries
    /// that will be allocated.
    /// 
    /// # Example
    /// 
    /// ```rust
    /// let icon = Icon::with_capacity(5);
    /// ```
    fn with_capacity(capacity: usize) -> Self;

    /// Returns the number of _entries_ contained in the icon.
    /// 
    /// # Example
    /// 
    /// ```rust
    /// let len = icon.len();
    /// ```
    fn len(&self) -> usize;

    /// Adds an individual entry to the icon.
    ///
    /// # Arguments
    ///
    /// * `filter` The resampling filter that will be used to re-scale `source`.
    /// * `source` A reference to the source image this entry will be based on.
    /// * `key` Information on the target entry.
    ///
    /// # Return Value
    ///
    /// * Returns `Err(EncodingError::AlreadyIncluded(_))` if the icon already contains
    ///   an entry associated with `key`.
    /// * Returns `Err(EncodingError::Resample(_))` if the resampling filter provided in
    ///   the `filter` argument fails produces results of dimensions other than the
    ///   ones specified by `key`.
    /// * Otherwise returns `Ok(())`.
    /// 
    /// # Example
    /// 
    /// ```rust
    /// fn main() -> io::Result<()> {
    ///     let image = Image::open("image.svg")?;
    ///     let icon = Icon::new();
    /// 
    ///     icon.add_entry(resample::linear, image, Key(32))?
    ///         .add_entry(resample::nearest, image, Key(64))?;
    /// 
    ///     Ok(())
    /// }
    /// ```
    fn add_entry<F: FnMut(&DynamicImage, u32) -> io::Result<DynamicImage>>(
        &mut self,
        filter: F,
        source: &Image,
        key: Self::Key,
    ) -> Result<&mut Self, EncodingError<Self::Key>>;

    /// Adds a series of entries to the icon.
    ///
    /// # Arguments
    ///
    /// * `filter` The resampling filter that will be used to re-scale `source`.
    /// * `source` A reference to the source image this entry will be based on.
    /// * `keys` A container for the information on the target entries.
    ///
    /// # Return Value
    ///
    /// * Returns `Err(EncodingError::AlreadyIncluded(_))` if the icon already contains an
    ///   entry associated with any of the items of `keys`.
    /// * Returns `Err(EncodingError::Resample(_))` if the resampling filter provided in
    ///   the `filter` argument fails or produces results of dimensions other than the
    ///   ones specified by the items of `keys`.
    /// * Otherwise returns `Ok(())`.
    /// 
    /// # Example
    /// 
    /// ```rust
    /// fn main() -> io::Result<()> {
    ///     let image = Image::open("image.svg")?;
    ///     let icon = Icon::new();
    /// 
    ///     icon.add_entries(
    ///         resample::linear,
    ///         image,
    ///         vec![Key(32), Key(64), Key(128)]
    ///     )?;
    /// 
    ///     Ok(())
    /// }
    /// ```
    fn add_entries<F: FnMut(&DynamicImage, u32) -> io::Result<DynamicImage>, I: IntoIterator<Item = Self::Key>>(
        &mut self,
        mut filter: F,
        source: &Image,
        keys: I,
    ) -> Result<&mut Self, EncodingError<Self::Key>> {
        for key in keys {
            self.add_entry(|src, size| filter(src, size), source, key)?;
        }

        Ok(self)
    }
}

/// The `Write` trait provides functionality for writing the
/// contents of an `Encode` into a `io::Write` implementor.
/// 
/// Usefull for _icon formats_ such as `.ico` and `.icns`
/// files.
pub trait Write: Encode {
    /// Writes the contents of the icon to `w`.
    ///
    /// # Example
    ///
    /// ```rust
    /// fn main() -> io::Result<()> {
    ///     let icon = Icon::new();
    ///
    ///     /* Process the icon */
    ///
    ///     let file = File::create("out.icns")?;
    ///     icon.write(file)
    /// }
    /// ```
    fn write<W: io::Write>(&mut self, w: &mut W) -> io::Result<()>;
}

/// The `Save` trait provides functionality for saving the
/// contents of an `Encode` to the local file system.
/// 
/// Usefull for _icon formats_ such as _favicon_.
pub trait Save: Encode {
    /// Writes the contents of the icon to disk.
    ///
    /// # Example
    ///
    /// ```rust
    /// use ikon::encode::{Encode, Save};
    ///  
    /// fn main() -> io::Result<()> {
    ///     let icon = Icon::new();
    ///
    ///     /* Process the icon */
    ///
    ///     icon.save("./output/")
    /// }
    /// ```
    fn save<P: AsRef<Path>>(&mut self, path: &P) -> io::Result<()>;
}

impl<T: Write> Save for T {
    #[inline]
    fn save<P: AsRef<Path>>(&mut self, path: &P) -> io::Result<()> {
        let mut file = BufWriter::new(File::create(path)?);
        self.write(&mut file)
    }
}

/// Converts _raster graphics_ to _PNG_-encoded buffers.
pub fn png(image: &DynamicImage) -> io::Result<Vec<u8>> {
    let data = image.to_rgba().into_raw();
    let mut output = Vec::with_capacity(data.len());

    let encoder = PNGEncoder::new(&mut output);
    encoder.encode(&data, image.width(), image.height(), ColorType::RGBA(8))?;

    Ok(output)
}

/// Converts _raster graphics_ to _BMP_-encoded buffers.
pub fn bmp(image: &DynamicImage) -> io::Result<Vec<u8>> {
    let data = image.to_rgba().into_raw();
    let mut output = Vec::with_capacity(data.len());

    let mut encoder = BMPEncoder::new(&mut output);
    encoder.encode(&data, image.width(), image.height(), ColorType::RGBA(8))?;

    Ok(output)
}

#[inline]
/// Converts _vector graphics_ to _UTF8_-encoded _SVG_ strings.
pub fn svg(image: &Tree) -> Vec<u8> {
    image.to_string(XML_OPTS).into_bytes()
}