aboutsummaryrefslogtreecommitdiff
path: root/vendor/image/src/codecs/ico/encoder.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/image/src/codecs/ico/encoder.rs')
-rw-r--r--vendor/image/src/codecs/ico/encoder.rs194
1 files changed, 194 insertions, 0 deletions
diff --git a/vendor/image/src/codecs/ico/encoder.rs b/vendor/image/src/codecs/ico/encoder.rs
new file mode 100644
index 0000000..dd5961b
--- /dev/null
+++ b/vendor/image/src/codecs/ico/encoder.rs
@@ -0,0 +1,194 @@
+use byteorder::{LittleEndian, WriteBytesExt};
+use std::borrow::Cow;
+use std::io::{self, Write};
+
+use crate::color::ColorType;
+use crate::error::{ImageError, ImageResult, ParameterError, ParameterErrorKind};
+use crate::image::ImageEncoder;
+
+use crate::codecs::png::PngEncoder;
+
+// Enum value indicating an ICO image (as opposed to a CUR image):
+const ICO_IMAGE_TYPE: u16 = 1;
+// The length of an ICO file ICONDIR structure, in bytes:
+const ICO_ICONDIR_SIZE: u32 = 6;
+// The length of an ICO file DIRENTRY structure, in bytes:
+const ICO_DIRENTRY_SIZE: u32 = 16;
+
+/// ICO encoder
+pub struct IcoEncoder<W: Write> {
+ w: W,
+}
+
+/// An ICO image entry
+pub struct IcoFrame<'a> {
+ // Pre-encoded PNG or BMP
+ encoded_image: Cow<'a, [u8]>,
+ // Stored as `0 => 256, n => n`
+ width: u8,
+ // Stored as `0 => 256, n => n`
+ height: u8,
+ color_type: ColorType,
+}
+
+impl<'a> IcoFrame<'a> {
+ /// Construct a new `IcoFrame` using a pre-encoded PNG or BMP
+ ///
+ /// The `width` and `height` must be between 1 and 256 (inclusive).
+ pub fn with_encoded(
+ encoded_image: impl Into<Cow<'a, [u8]>>,
+ width: u32,
+ height: u32,
+ color_type: ColorType,
+ ) -> ImageResult<Self> {
+ let encoded_image = encoded_image.into();
+
+ if !(1..=256).contains(&width) {
+ return Err(ImageError::Parameter(ParameterError::from_kind(
+ ParameterErrorKind::Generic(format!(
+ "the image width must be `1..=256`, instead width {} was provided",
+ width,
+ )),
+ )));
+ }
+
+ if !(1..=256).contains(&height) {
+ return Err(ImageError::Parameter(ParameterError::from_kind(
+ ParameterErrorKind::Generic(format!(
+ "the image height must be `1..=256`, instead height {} was provided",
+ height,
+ )),
+ )));
+ }
+
+ Ok(Self {
+ encoded_image,
+ width: width as u8,
+ height: height as u8,
+ color_type,
+ })
+ }
+
+ /// Construct a new `IcoFrame` by encoding `buf` as a PNG
+ ///
+ /// The `width` and `height` must be between 1 and 256 (inclusive)
+ pub fn as_png(buf: &[u8], width: u32, height: u32, color_type: ColorType) -> ImageResult<Self> {
+ let mut image_data: Vec<u8> = Vec::new();
+ PngEncoder::new(&mut image_data).write_image(buf, width, height, color_type)?;
+
+ let frame = Self::with_encoded(image_data, width, height, color_type)?;
+ Ok(frame)
+ }
+}
+
+impl<W: Write> IcoEncoder<W> {
+ /// Create a new encoder that writes its output to ```w```.
+ pub fn new(w: W) -> IcoEncoder<W> {
+ IcoEncoder { w }
+ }
+
+ /// Encodes the image ```image``` that has dimensions ```width``` and
+ /// ```height``` and ```ColorType``` ```c```. The dimensions of the image
+ /// must be between 1 and 256 (inclusive) or an error will be returned.
+ ///
+ /// Expects data to be big endian.
+ #[deprecated = "Use `IcoEncoder::write_image` instead. Beware that `write_image` has a different endianness convention"]
+ pub fn encode(self, data: &[u8], width: u32, height: u32, color: ColorType) -> ImageResult<()> {
+ let mut image_data: Vec<u8> = Vec::new();
+ #[allow(deprecated)]
+ PngEncoder::new(&mut image_data).encode(data, width, height, color)?;
+
+ let image = IcoFrame::with_encoded(&image_data, width, height, color)?;
+ self.encode_images(&[image])
+ }
+
+ /// Takes some [`IcoFrame`]s and encodes them into an ICO.
+ ///
+ /// `images` is a list of images, usually ordered by dimension, which
+ /// must be between 1 and 65535 (inclusive) in length.
+ pub fn encode_images(mut self, images: &[IcoFrame<'_>]) -> ImageResult<()> {
+ if !(1..=usize::from(u16::MAX)).contains(&images.len()) {
+ return Err(ImageError::Parameter(ParameterError::from_kind(
+ ParameterErrorKind::Generic(format!(
+ "the number of images must be `1..=u16::MAX`, instead {} images were provided",
+ images.len(),
+ )),
+ )));
+ }
+ let num_images = images.len() as u16;
+
+ let mut offset = ICO_ICONDIR_SIZE + (ICO_DIRENTRY_SIZE * (images.len() as u32));
+ write_icondir(&mut self.w, num_images)?;
+ for image in images {
+ write_direntry(
+ &mut self.w,
+ image.width,
+ image.height,
+ image.color_type,
+ offset,
+ image.encoded_image.len() as u32,
+ )?;
+
+ offset += image.encoded_image.len() as u32;
+ }
+ for image in images {
+ self.w.write_all(&image.encoded_image)?;
+ }
+ Ok(())
+ }
+}
+
+impl<W: Write> ImageEncoder for IcoEncoder<W> {
+ /// Write an ICO image with the specified width, height, and color type.
+ ///
+ /// For color types with 16-bit per channel or larger, the contents of `buf` should be in
+ /// native endian.
+ ///
+ /// WARNING: In image 0.23.14 and earlier this method erroneously expected buf to be in big endian.
+ fn write_image(
+ self,
+ buf: &[u8],
+ width: u32,
+ height: u32,
+ color_type: ColorType,
+ ) -> ImageResult<()> {
+ let image = IcoFrame::as_png(buf, width, height, color_type)?;
+ self.encode_images(&[image])
+ }
+}
+
+fn write_icondir<W: Write>(w: &mut W, num_images: u16) -> io::Result<()> {
+ // Reserved field (must be zero):
+ w.write_u16::<LittleEndian>(0)?;
+ // Image type (ICO or CUR):
+ w.write_u16::<LittleEndian>(ICO_IMAGE_TYPE)?;
+ // Number of images in the file:
+ w.write_u16::<LittleEndian>(num_images)?;
+ Ok(())
+}
+
+fn write_direntry<W: Write>(
+ w: &mut W,
+ width: u8,
+ height: u8,
+ color: ColorType,
+ data_start: u32,
+ data_size: u32,
+) -> io::Result<()> {
+ // Image dimensions:
+ w.write_u8(width)?;
+ w.write_u8(height)?;
+ // Number of colors in palette (or zero for no palette):
+ w.write_u8(0)?;
+ // Reserved field (must be zero):
+ w.write_u8(0)?;
+ // Color planes:
+ w.write_u16::<LittleEndian>(0)?;
+ // Bits per pixel:
+ w.write_u16::<LittleEndian>(color.bits_per_pixel())?;
+ // Image data size, in bytes:
+ w.write_u32::<LittleEndian>(data_size)?;
+ // Image data offset, in bytes:
+ w.write_u32::<LittleEndian>(data_start)?;
+ Ok(())
+}