aboutsummaryrefslogtreecommitdiff
path: root/vendor/image/src/codecs/pnm/encoder.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/image/src/codecs/pnm/encoder.rs')
-rw-r--r--vendor/image/src/codecs/pnm/encoder.rs673
1 files changed, 0 insertions, 673 deletions
diff --git a/vendor/image/src/codecs/pnm/encoder.rs b/vendor/image/src/codecs/pnm/encoder.rs
deleted file mode 100644
index 9f823d0..0000000
--- a/vendor/image/src/codecs/pnm/encoder.rs
+++ /dev/null
@@ -1,673 +0,0 @@
-//! Encoding of PNM Images
-use std::fmt;
-use std::io;
-
-use std::io::Write;
-
-use super::AutoBreak;
-use super::{ArbitraryHeader, ArbitraryTuplType, BitmapHeader, GraymapHeader, PixmapHeader};
-use super::{HeaderRecord, PnmHeader, PnmSubtype, SampleEncoding};
-use crate::color::{ColorType, ExtendedColorType};
-use crate::error::{
- ImageError, ImageResult, ParameterError, ParameterErrorKind, UnsupportedError,
- UnsupportedErrorKind,
-};
-use crate::image::{ImageEncoder, ImageFormat};
-
-use byteorder::{BigEndian, WriteBytesExt};
-
-enum HeaderStrategy {
- Dynamic,
- Subtype(PnmSubtype),
- Chosen(PnmHeader),
-}
-
-#[derive(Clone, Copy)]
-pub enum FlatSamples<'a> {
- U8(&'a [u8]),
- U16(&'a [u16]),
-}
-
-/// Encodes images to any of the `pnm` image formats.
-pub struct PnmEncoder<W: Write> {
- writer: W,
- header: HeaderStrategy,
-}
-
-/// Encapsulate the checking system in the type system. Non of the fields are actually accessed
-/// but requiring them forces us to validly construct the struct anyways.
-struct CheckedImageBuffer<'a> {
- _image: FlatSamples<'a>,
- _width: u32,
- _height: u32,
- _color: ExtendedColorType,
-}
-
-// Check the header against the buffer. Each struct produces the next after a check.
-struct UncheckedHeader<'a> {
- header: &'a PnmHeader,
-}
-
-struct CheckedDimensions<'a> {
- unchecked: UncheckedHeader<'a>,
- width: u32,
- height: u32,
-}
-
-struct CheckedHeaderColor<'a> {
- dimensions: CheckedDimensions<'a>,
- color: ExtendedColorType,
-}
-
-struct CheckedHeader<'a> {
- color: CheckedHeaderColor<'a>,
- encoding: TupleEncoding<'a>,
- _image: CheckedImageBuffer<'a>,
-}
-
-enum TupleEncoding<'a> {
- PbmBits {
- samples: FlatSamples<'a>,
- width: u32,
- },
- Ascii {
- samples: FlatSamples<'a>,
- },
- Bytes {
- samples: FlatSamples<'a>,
- },
-}
-
-impl<W: Write> PnmEncoder<W> {
- /// Create new PnmEncoder from the `writer`.
- ///
- /// The encoded images will have some `pnm` format. If more control over the image type is
- /// required, use either one of `with_subtype` or `with_header`. For more information on the
- /// behaviour, see `with_dynamic_header`.
- pub fn new(writer: W) -> Self {
- PnmEncoder {
- writer,
- header: HeaderStrategy::Dynamic,
- }
- }
-
- /// Encode a specific pnm subtype image.
- ///
- /// The magic number and encoding type will be chosen as provided while the rest of the header
- /// data will be generated dynamically. Trying to encode incompatible images (e.g. encoding an
- /// RGB image as Graymap) will result in an error.
- ///
- /// This will overwrite the effect of earlier calls to `with_header` and `with_dynamic_header`.
- pub fn with_subtype(self, subtype: PnmSubtype) -> Self {
- PnmEncoder {
- writer: self.writer,
- header: HeaderStrategy::Subtype(subtype),
- }
- }
-
- /// Enforce the use of a chosen header.
- ///
- /// While this option gives the most control over the actual written data, the encoding process
- /// will error in case the header data and image parameters do not agree. It is the users
- /// obligation to ensure that the width and height are set accordingly, for example.
- ///
- /// Choose this option if you want a lossless decoding/encoding round trip.
- ///
- /// This will overwrite the effect of earlier calls to `with_subtype` and `with_dynamic_header`.
- pub fn with_header(self, header: PnmHeader) -> Self {
- PnmEncoder {
- writer: self.writer,
- header: HeaderStrategy::Chosen(header),
- }
- }
-
- /// Create the header dynamically for each image.
- ///
- /// This is the default option upon creation of the encoder. With this, most images should be
- /// encodable but the specific format chosen is out of the users control. The pnm subtype is
- /// chosen arbitrarily by the library.
- ///
- /// This will overwrite the effect of earlier calls to `with_subtype` and `with_header`.
- pub fn with_dynamic_header(self) -> Self {
- PnmEncoder {
- writer: self.writer,
- header: HeaderStrategy::Dynamic,
- }
- }
-
- /// Encode an image whose samples are represented as `u8`.
- ///
- /// Some `pnm` subtypes are incompatible with some color options, a chosen header most
- /// certainly with any deviation from the original decoded image.
- pub fn encode<'s, S>(
- &mut self,
- image: S,
- width: u32,
- height: u32,
- color: ColorType,
- ) -> ImageResult<()>
- where
- S: Into<FlatSamples<'s>>,
- {
- let image = image.into();
- match self.header {
- HeaderStrategy::Dynamic => {
- self.write_dynamic_header(image, width, height, color.into())
- }
- HeaderStrategy::Subtype(subtype) => {
- self.write_subtyped_header(subtype, image, width, height, color.into())
- }
- HeaderStrategy::Chosen(ref header) => Self::write_with_header(
- &mut self.writer,
- header,
- image,
- width,
- height,
- color.into(),
- ),
- }
- }
-
- /// Choose any valid pnm format that the image can be expressed in and write its header.
- ///
- /// Returns how the body should be written if successful.
- fn write_dynamic_header(
- &mut self,
- image: FlatSamples,
- width: u32,
- height: u32,
- color: ExtendedColorType,
- ) -> ImageResult<()> {
- let depth = u32::from(color.channel_count());
- let (maxval, tupltype) = match color {
- ExtendedColorType::L1 => (1, ArbitraryTuplType::BlackAndWhite),
- ExtendedColorType::L8 => (0xff, ArbitraryTuplType::Grayscale),
- ExtendedColorType::L16 => (0xffff, ArbitraryTuplType::Grayscale),
- ExtendedColorType::La1 => (1, ArbitraryTuplType::BlackAndWhiteAlpha),
- ExtendedColorType::La8 => (0xff, ArbitraryTuplType::GrayscaleAlpha),
- ExtendedColorType::La16 => (0xffff, ArbitraryTuplType::GrayscaleAlpha),
- ExtendedColorType::Rgb8 => (0xff, ArbitraryTuplType::RGB),
- ExtendedColorType::Rgb16 => (0xffff, ArbitraryTuplType::RGB),
- ExtendedColorType::Rgba8 => (0xff, ArbitraryTuplType::RGBAlpha),
- ExtendedColorType::Rgba16 => (0xffff, ArbitraryTuplType::RGBAlpha),
- _ => {
- return Err(ImageError::Unsupported(
- UnsupportedError::from_format_and_kind(
- ImageFormat::Pnm.into(),
- UnsupportedErrorKind::Color(color),
- ),
- ))
- }
- };
-
- let header = PnmHeader {
- decoded: HeaderRecord::Arbitrary(ArbitraryHeader {
- width,
- height,
- depth,
- maxval,
- tupltype: Some(tupltype),
- }),
- encoded: None,
- };
-
- Self::write_with_header(&mut self.writer, &header, image, width, height, color)
- }
-
- /// Try to encode the image with the chosen format, give its corresponding pixel encoding type.
- fn write_subtyped_header(
- &mut self,
- subtype: PnmSubtype,
- image: FlatSamples,
- width: u32,
- height: u32,
- color: ExtendedColorType,
- ) -> ImageResult<()> {
- let header = match (subtype, color) {
- (PnmSubtype::ArbitraryMap, color) => {
- return self.write_dynamic_header(image, width, height, color)
- }
- (PnmSubtype::Pixmap(encoding), ExtendedColorType::Rgb8) => PnmHeader {
- decoded: HeaderRecord::Pixmap(PixmapHeader {
- encoding,
- width,
- height,
- maxval: 255,
- }),
- encoded: None,
- },
- (PnmSubtype::Graymap(encoding), ExtendedColorType::L8) => PnmHeader {
- decoded: HeaderRecord::Graymap(GraymapHeader {
- encoding,
- width,
- height,
- maxwhite: 255,
- }),
- encoded: None,
- },
- (PnmSubtype::Bitmap(encoding), ExtendedColorType::L8)
- | (PnmSubtype::Bitmap(encoding), ExtendedColorType::L1) => PnmHeader {
- decoded: HeaderRecord::Bitmap(BitmapHeader {
- encoding,
- width,
- height,
- }),
- encoded: None,
- },
- (_, _) => {
- return Err(ImageError::Parameter(ParameterError::from_kind(
- ParameterErrorKind::Generic(
- "Color type can not be represented in the chosen format".to_owned(),
- ),
- )));
- }
- };
-
- Self::write_with_header(&mut self.writer, &header, image, width, height, color)
- }
-
- /// Try to encode the image with the chosen header, checking if values are correct.
- ///
- /// Returns how the body should be written if successful.
- fn write_with_header(
- writer: &mut dyn Write,
- header: &PnmHeader,
- image: FlatSamples,
- width: u32,
- height: u32,
- color: ExtendedColorType,
- ) -> ImageResult<()> {
- let unchecked = UncheckedHeader { header };
-
- unchecked
- .check_header_dimensions(width, height)?
- .check_header_color(color)?
- .check_sample_values(image)?
- .write_header(writer)?
- .write_image(writer)
- }
-}
-
-impl<W: Write> ImageEncoder for PnmEncoder<W> {
- fn write_image(
- mut self,
- buf: &[u8],
- width: u32,
- height: u32,
- color_type: ColorType,
- ) -> ImageResult<()> {
- self.encode(buf, width, height, color_type)
- }
-}
-
-impl<'a> CheckedImageBuffer<'a> {
- fn check(
- image: FlatSamples<'a>,
- width: u32,
- height: u32,
- color: ExtendedColorType,
- ) -> ImageResult<CheckedImageBuffer<'a>> {
- let components = color.channel_count() as usize;
- let uwidth = width as usize;
- let uheight = height as usize;
- let expected_len = components
- .checked_mul(uwidth)
- .and_then(|v| v.checked_mul(uheight));
- if Some(image.len()) != expected_len {
- // Image buffer does not correspond to size and colour.
- return Err(ImageError::Parameter(ParameterError::from_kind(
- ParameterErrorKind::DimensionMismatch,
- )));
- }
- Ok(CheckedImageBuffer {
- _image: image,
- _width: width,
- _height: height,
- _color: color,
- })
- }
-}
-
-impl<'a> UncheckedHeader<'a> {
- fn check_header_dimensions(
- self,
- width: u32,
- height: u32,
- ) -> ImageResult<CheckedDimensions<'a>> {
- if self.header.width() != width || self.header.height() != height {
- // Chosen header does not match Image dimensions.
- return Err(ImageError::Parameter(ParameterError::from_kind(
- ParameterErrorKind::DimensionMismatch,
- )));
- }
-
- Ok(CheckedDimensions {
- unchecked: self,
- width,
- height,
- })
- }
-}
-
-impl<'a> CheckedDimensions<'a> {
- // Check color compatibility with the header. This will only error when we are certain that
- // the combination is bogus (e.g. combining Pixmap and Palette) but allows uncertain
- // combinations (basically a ArbitraryTuplType::Custom with any color of fitting depth).
- fn check_header_color(self, color: ExtendedColorType) -> ImageResult<CheckedHeaderColor<'a>> {
- let components = u32::from(color.channel_count());
-
- match *self.unchecked.header {
- PnmHeader {
- decoded: HeaderRecord::Bitmap(_),
- ..
- } => match color {
- ExtendedColorType::L1 | ExtendedColorType::L8 | ExtendedColorType::L16 => (),
- _ => {
- return Err(ImageError::Parameter(ParameterError::from_kind(
- ParameterErrorKind::Generic(
- "PBM format only support luma color types".to_owned(),
- ),
- )))
- }
- },
- PnmHeader {
- decoded: HeaderRecord::Graymap(_),
- ..
- } => match color {
- ExtendedColorType::L1 | ExtendedColorType::L8 | ExtendedColorType::L16 => (),
- _ => {
- return Err(ImageError::Parameter(ParameterError::from_kind(
- ParameterErrorKind::Generic(
- "PGM format only support luma color types".to_owned(),
- ),
- )))
- }
- },
- PnmHeader {
- decoded: HeaderRecord::Pixmap(_),
- ..
- } => match color {
- ExtendedColorType::Rgb8 => (),
- _ => {
- return Err(ImageError::Parameter(ParameterError::from_kind(
- ParameterErrorKind::Generic(
- "PPM format only support ExtendedColorType::Rgb8".to_owned(),
- ),
- )))
- }
- },
- PnmHeader {
- decoded:
- HeaderRecord::Arbitrary(ArbitraryHeader {
- depth,
- ref tupltype,
- ..
- }),
- ..
- } => match (tupltype, color) {
- (&Some(ArbitraryTuplType::BlackAndWhite), ExtendedColorType::L1) => (),
- (&Some(ArbitraryTuplType::BlackAndWhiteAlpha), ExtendedColorType::La8) => (),
-
- (&Some(ArbitraryTuplType::Grayscale), ExtendedColorType::L1) => (),
- (&Some(ArbitraryTuplType::Grayscale), ExtendedColorType::L8) => (),
- (&Some(ArbitraryTuplType::Grayscale), ExtendedColorType::L16) => (),
- (&Some(ArbitraryTuplType::GrayscaleAlpha), ExtendedColorType::La8) => (),
-
- (&Some(ArbitraryTuplType::RGB), ExtendedColorType::Rgb8) => (),
- (&Some(ArbitraryTuplType::RGBAlpha), ExtendedColorType::Rgba8) => (),
-
- (&None, _) if depth == components => (),
- (&Some(ArbitraryTuplType::Custom(_)), _) if depth == components => (),
- _ if depth != components => {
- return Err(ImageError::Parameter(ParameterError::from_kind(
- ParameterErrorKind::Generic(format!(
- "Depth mismatch: header {} vs. color {}",
- depth, components
- )),
- )))
- }
- _ => {
- return Err(ImageError::Parameter(ParameterError::from_kind(
- ParameterErrorKind::Generic(
- "Invalid color type for selected PAM color type".to_owned(),
- ),
- )))
- }
- },
- }
-
- Ok(CheckedHeaderColor {
- dimensions: self,
- color,
- })
- }
-}
-
-impl<'a> CheckedHeaderColor<'a> {
- fn check_sample_values(self, image: FlatSamples<'a>) -> ImageResult<CheckedHeader<'a>> {
- let header_maxval = match self.dimensions.unchecked.header.decoded {
- HeaderRecord::Bitmap(_) => 1,
- HeaderRecord::Graymap(GraymapHeader { maxwhite, .. }) => maxwhite,
- HeaderRecord::Pixmap(PixmapHeader { maxval, .. }) => maxval,
- HeaderRecord::Arbitrary(ArbitraryHeader { maxval, .. }) => maxval,
- };
-
- // We trust the image color bit count to be correct at least.
- let max_sample = match self.color {
- ExtendedColorType::Unknown(n) if n <= 16 => (1 << n) - 1,
- ExtendedColorType::L1 => 1,
- ExtendedColorType::L8
- | ExtendedColorType::La8
- | ExtendedColorType::Rgb8
- | ExtendedColorType::Rgba8
- | ExtendedColorType::Bgr8
- | ExtendedColorType::Bgra8 => 0xff,
- ExtendedColorType::L16
- | ExtendedColorType::La16
- | ExtendedColorType::Rgb16
- | ExtendedColorType::Rgba16 => 0xffff,
- _ => {
- // Unsupported target color type.
- return Err(ImageError::Unsupported(
- UnsupportedError::from_format_and_kind(
- ImageFormat::Pnm.into(),
- UnsupportedErrorKind::Color(self.color),
- ),
- ));
- }
- };
-
- // Avoid the performance heavy check if possible, e.g. if the header has been chosen by us.
- if header_maxval < max_sample && !image.all_smaller(header_maxval) {
- // Sample value greater than allowed for chosen header.
- return Err(ImageError::Unsupported(
- UnsupportedError::from_format_and_kind(
- ImageFormat::Pnm.into(),
- UnsupportedErrorKind::GenericFeature(
- "Sample value greater than allowed for chosen header".to_owned(),
- ),
- ),
- ));
- }
-
- let encoding = image.encoding_for(&self.dimensions.unchecked.header.decoded);
-
- let image = CheckedImageBuffer::check(
- image,
- self.dimensions.width,
- self.dimensions.height,
- self.color,
- )?;
-
- Ok(CheckedHeader {
- color: self,
- encoding,
- _image: image,
- })
- }
-}
-
-impl<'a> CheckedHeader<'a> {
- fn write_header(self, writer: &mut dyn Write) -> ImageResult<TupleEncoding<'a>> {
- self.header().write(writer)?;
- Ok(self.encoding)
- }
-
- fn header(&self) -> &PnmHeader {
- self.color.dimensions.unchecked.header
- }
-}
-
-struct SampleWriter<'a>(&'a mut dyn Write);
-
-impl<'a> SampleWriter<'a> {
- fn write_samples_ascii<V>(self, samples: V) -> io::Result<()>
- where
- V: Iterator,
- V::Item: fmt::Display,
- {
- let mut auto_break_writer = AutoBreak::new(self.0, 70);
- for value in samples {
- write!(auto_break_writer, "{} ", value)?;
- }
- auto_break_writer.flush()
- }
-
- fn write_pbm_bits<V>(self, samples: &[V], width: u32) -> io::Result<()>
- /* Default gives 0 for all primitives. TODO: replace this with `Zeroable` once it hits stable */
- where
- V: Default + Eq + Copy,
- {
- // The length of an encoded scanline
- let line_width = (width - 1) / 8 + 1;
-
- // We'll be writing single bytes, so buffer
- let mut line_buffer = Vec::with_capacity(line_width as usize);
-
- for line in samples.chunks(width as usize) {
- for byte_bits in line.chunks(8) {
- let mut byte = 0u8;
- for i in 0..8 {
- // Black pixels are encoded as 1s
- if let Some(&v) = byte_bits.get(i) {
- if v == V::default() {
- byte |= 1u8 << (7 - i)
- }
- }
- }
- line_buffer.push(byte)
- }
- self.0.write_all(line_buffer.as_slice())?;
- line_buffer.clear();
- }
-
- self.0.flush()
- }
-}
-
-impl<'a> FlatSamples<'a> {
- fn len(&self) -> usize {
- match *self {
- FlatSamples::U8(arr) => arr.len(),
- FlatSamples::U16(arr) => arr.len(),
- }
- }
-
- fn all_smaller(&self, max_val: u32) -> bool {
- match *self {
- FlatSamples::U8(arr) => arr.iter().any(|&val| u32::from(val) > max_val),
- FlatSamples::U16(arr) => arr.iter().any(|&val| u32::from(val) > max_val),
- }
- }
-
- fn encoding_for(&self, header: &HeaderRecord) -> TupleEncoding<'a> {
- match *header {
- HeaderRecord::Bitmap(BitmapHeader {
- encoding: SampleEncoding::Binary,
- width,
- ..
- }) => TupleEncoding::PbmBits {
- samples: *self,
- width,
- },
-
- HeaderRecord::Bitmap(BitmapHeader {
- encoding: SampleEncoding::Ascii,
- ..
- }) => TupleEncoding::Ascii { samples: *self },
-
- HeaderRecord::Arbitrary(_) => TupleEncoding::Bytes { samples: *self },
-
- HeaderRecord::Graymap(GraymapHeader {
- encoding: SampleEncoding::Ascii,
- ..
- })
- | HeaderRecord::Pixmap(PixmapHeader {
- encoding: SampleEncoding::Ascii,
- ..
- }) => TupleEncoding::Ascii { samples: *self },
-
- HeaderRecord::Graymap(GraymapHeader {
- encoding: SampleEncoding::Binary,
- ..
- })
- | HeaderRecord::Pixmap(PixmapHeader {
- encoding: SampleEncoding::Binary,
- ..
- }) => TupleEncoding::Bytes { samples: *self },
- }
- }
-}
-
-impl<'a> From<&'a [u8]> for FlatSamples<'a> {
- fn from(samples: &'a [u8]) -> Self {
- FlatSamples::U8(samples)
- }
-}
-
-impl<'a> From<&'a [u16]> for FlatSamples<'a> {
- fn from(samples: &'a [u16]) -> Self {
- FlatSamples::U16(samples)
- }
-}
-
-impl<'a> TupleEncoding<'a> {
- fn write_image(&self, writer: &mut dyn Write) -> ImageResult<()> {
- match *self {
- TupleEncoding::PbmBits {
- samples: FlatSamples::U8(samples),
- width,
- } => SampleWriter(writer)
- .write_pbm_bits(samples, width)
- .map_err(ImageError::IoError),
- TupleEncoding::PbmBits {
- samples: FlatSamples::U16(samples),
- width,
- } => SampleWriter(writer)
- .write_pbm_bits(samples, width)
- .map_err(ImageError::IoError),
-
- TupleEncoding::Bytes {
- samples: FlatSamples::U8(samples),
- } => writer.write_all(samples).map_err(ImageError::IoError),
- TupleEncoding::Bytes {
- samples: FlatSamples::U16(samples),
- } => samples.iter().try_for_each(|&sample| {
- writer
- .write_u16::<BigEndian>(sample)
- .map_err(ImageError::IoError)
- }),
-
- TupleEncoding::Ascii {
- samples: FlatSamples::U8(samples),
- } => SampleWriter(writer)
- .write_samples_ascii(samples.iter())
- .map_err(ImageError::IoError),
- TupleEncoding::Ascii {
- samples: FlatSamples::U16(samples),
- } => SampleWriter(writer)
- .write_samples_ascii(samples.iter())
- .map_err(ImageError::IoError),
- }
- }
-}