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}