image_extras/
pcx.rs

1//! Decoding of PCX Images
2//!
3//! PCX (PiCture eXchange) Format is an obsolete image format from the 1980s.
4//!
5//! # Related Links
6//! * <https://en.wikipedia.org/wiki/PCX> - The PCX format on Wikipedia
7
8use std::io::{self, BufRead, Read, Seek};
9use std::iter;
10
11use image::{ColorType, ExtendedColorType, ImageDecoder, ImageError, ImageResult};
12
13/// Decoder for PCX images.
14pub struct PCXDecoder<R>
15where
16    R: Read,
17{
18    dimensions: (u32, u32),
19    inner: pcx::Reader<R>,
20}
21
22impl<R> PCXDecoder<R>
23where
24    R: BufRead + Seek,
25{
26    /// Create a new `PCXDecoder`.
27    pub fn new(r: R) -> Result<PCXDecoder<R>, ImageError> {
28        let inner = pcx::Reader::new(r).map_err(convert_pcx_decode_error)?;
29        let dimensions = (u32::from(inner.width()), u32::from(inner.height()));
30
31        Ok(PCXDecoder { dimensions, inner })
32    }
33}
34
35fn convert_pcx_decode_error(err: io::Error) -> ImageError {
36    ImageError::IoError(err)
37}
38
39impl<R: BufRead + Seek> ImageDecoder for PCXDecoder<R> {
40    fn dimensions(&self) -> (u32, u32) {
41        self.dimensions
42    }
43
44    fn color_type(&self) -> ColorType {
45        ColorType::Rgb8
46    }
47
48    fn original_color_type(&self) -> ExtendedColorType {
49        if self.inner.is_paletted() {
50            return ExtendedColorType::Unknown(self.inner.header.bit_depth);
51        }
52
53        match (
54            self.inner.header.number_of_color_planes,
55            self.inner.header.bit_depth,
56        ) {
57            (1, 1) => ExtendedColorType::L1,
58            (1, 2) => ExtendedColorType::L2,
59            (1, 4) => ExtendedColorType::L4,
60            (1, 8) => ExtendedColorType::L8,
61            (3, 1) => ExtendedColorType::Rgb1,
62            (3, 2) => ExtendedColorType::Rgb2,
63            (3, 4) => ExtendedColorType::Rgb4,
64            (3, 8) => ExtendedColorType::Rgb8,
65            (4, 1) => ExtendedColorType::Rgba1,
66            (4, 2) => ExtendedColorType::Rgba2,
67            (4, 4) => ExtendedColorType::Rgba4,
68            (4, 8) => ExtendedColorType::Rgba8,
69            (_, _) => unreachable!(),
70        }
71    }
72
73    fn read_image(mut self, buf: &mut [u8]) -> ImageResult<()> {
74        assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes()));
75
76        let height = self.inner.height() as usize;
77        let width = self.inner.width() as usize;
78
79        match self.inner.palette_length() {
80            // No palette to interpret, so we can just write directly to buf
81            None => {
82                for i in 0..height {
83                    let offset = i * 3 * width;
84                    self.inner
85                        .next_row_rgb(&mut buf[offset..offset + (width * 3)])
86                        .map_err(convert_pcx_decode_error)?;
87                }
88            }
89
90            // We need to convert from the palette colours to RGB values inline,
91            // but the pcx crate can't give us the palette first. Work around it
92            // by taking the paletted image into a buffer, then converting it to
93            // RGB8 after.
94            Some(palette_length) => {
95                let mut pal_buf: Vec<u8> = iter::repeat_n(0, height * width).collect();
96
97                for i in 0..height {
98                    let offset = i * width;
99                    self.inner
100                        .next_row_paletted(&mut pal_buf[offset..offset + width])
101                        .map_err(convert_pcx_decode_error)?;
102                }
103
104                let mut palette: Vec<u8> =
105                    std::iter::repeat_n(0, 3 * palette_length as usize).collect();
106                self.inner
107                    .read_palette(&mut palette[..])
108                    .map_err(convert_pcx_decode_error)?;
109
110                for i in 0..height {
111                    for j in 0..width {
112                        let pixel = pal_buf[i * width + j] as usize;
113                        let offset = i * width * 3 + j * 3;
114
115                        buf[offset] = palette[pixel * 3];
116                        buf[offset + 1] = palette[pixel * 3 + 1];
117                        buf[offset + 2] = palette[pixel * 3 + 2];
118                    }
119                }
120            }
121        }
122
123        Ok(())
124    }
125
126    fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
127        (*self).read_image(buf)
128    }
129}