Skip to main content

rasterrocket_encode/
lib.rs

1//! Image encoding — write a `raster::Bitmap<P>` to PPM, PGM, PBM, and PNG.
2//!
3//! # Supported formats
4//!
5//! | Function | Format | Accepted pixel types |
6//! |----------|--------|--------------------|
7//! | [`write_ppm`] | Netpbm P6 binary | `Rgb8`, `Bgr8`, `Rgba8` (`Xbgr8`), `Cmyk8`, `DeviceN8` |
8//! | [`write_pgm`] | Netpbm P5 binary | `Gray8` |
9//! | [`write_pbm`] | Netpbm P4 binary | `Gray8` (0 = white, non-zero = black) |
10//! | [`write_png`] | PNG | `Rgb8`, `Gray8`, `Rgba8` |
11//!
12//! All functions consume the output sink (`W: Write`).  Wrap in
13//! [`std::io::BufWriter`] at the call site if buffering is needed.
14//!
15//! # CMYK handling
16//!
17//! Neither PPM nor PNG natively supports CMYK.  [`write_ppm`] converts
18//! CMYK/`DeviceN` to RGB via the naïve subtractive ink-density formula
19//! `R = 255 − C − K` (PDF §10.3.3).  For ICC-accurate colour, convert to
20//! `Rgb8` before encoding.
21
22pub mod pbm;
23pub mod pgm;
24pub mod png;
25pub mod ppm;
26
27pub use pbm::write_pbm;
28pub use pgm::write_pgm;
29pub use png::write_png;
30pub use ppm::write_ppm;
31
32use std::io;
33
34/// Errors that can occur during encoding.
35#[derive(Debug)]
36pub enum EncodeError {
37    /// An I/O error writing to the output sink.
38    Io(io::Error),
39    /// The pixel mode is not supported by the chosen encoder.
40    ///
41    /// The message describes what the caller should do instead.
42    UnsupportedMode(&'static str),
43    /// An internal error from the `png` encoder (non-I/O).
44    PngEncoder(::png::EncodingError),
45}
46
47impl std::fmt::Display for EncodeError {
48    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49        match self {
50            Self::Io(e) => write!(f, "I/O error: {e}"),
51            Self::UnsupportedMode(m) => write!(f, "pixel mode not supported: {m}"),
52            Self::PngEncoder(e) => write!(f, "PNG encoder error: {e}"),
53        }
54    }
55}
56
57impl std::error::Error for EncodeError {
58    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
59        match self {
60            Self::Io(e) => Some(e),
61            Self::PngEncoder(e) => Some(e),
62            Self::UnsupportedMode(_) => None,
63        }
64    }
65}
66
67impl From<io::Error> for EncodeError {
68    fn from(e: io::Error) -> Self {
69        Self::Io(e)
70    }
71}
72
73impl From<::png::EncodingError> for EncodeError {
74    fn from(e: ::png::EncodingError) -> Self {
75        // Unwrap the I/O layer so EncodeError::Io is the canonical I/O path.
76        match e {
77            ::png::EncodingError::IoError(io_err) => Self::Io(io_err),
78            other => Self::PngEncoder(other),
79        }
80    }
81}