bmp_encoder/
lib.rs

1#![warn(warnings)]
2#![cfg_attr(test, deny(warnings))]
3
4//! A small library for reading and writing BMP images.
5//!
6//! The library supports uncompressed BMP Version 3 images.
7//! The different decoding and encoding schemes is shown in the table below.
8//!
9//! |Scheme | Decoding | Encoding | Compression |
10//! |-------|----------|----------|-------------|
11//! | 24 bpp| ✓        | ✓        | No          |
12//! | 8 bpp | ✓        | ✗        | No          |
13//! | 4 bpp | ✓        | ✗        | No          |
14//! | 1 bpp | ✓        | ✗        | No          |
15//!
16//! # Example
17//!
18//! ```
19//! #[macro_use]
20//! extern crate bmp;
21//! use bmp::{Image, Pixel};
22//!
23//! fn main() {
24//!     let mut img = Image::new(256, 256);
25//!
26//!     for (x, y) in img.coordinates() {
27//!         img.set_pixel(x, y, px!(x, y, 200));
28//!     }
29//!     let _ = img.save("img.bmp");
30//! }
31//! ```
32//!
33
34extern crate byteorder;
35
36use std::convert::{AsRef};
37use std::error::Error;
38use std::fmt;
39use std::fs;
40use std::io;
41use std::io::{Cursor, Read, Write};
42use std::iter::Iterator;
43
44use ::CompressionType::*;
45use ::BmpVersion::*;
46
47pub use decoder::{BmpError, BmpErrorKind, BmpResult};
48
49#[cfg(test)]
50mod tests;
51
52/// The pixel data used in the `Image`.
53///
54/// It has three values for the `red`, `blue` and `green` color channels, respectively.
55#[derive(Clone, Copy, Debug, Eq, PartialEq)]
56pub struct Pixel {
57    pub r: u8,
58    pub g: u8,
59    pub b: u8
60}
61
62/// Macro to generate a `Pixel` from `r`, `g` and `b` values.
63#[macro_export]
64macro_rules! px {
65    ($r:expr, $g:expr, $b:expr) => {
66        Pixel { r: $r as u8, g: $g as u8, b: $b as u8 }
67    }
68}
69
70macro_rules! file_size {
71    ($bpp:expr, $width:expr, $height:expr) => {{
72        let header_size = 2 + 12 + 40;
73        let row_size = (($bpp as f32 * $width as f32 + 31.0) / 32.0).floor() as u32 * 4;
74        (header_size as u32, $height as u32 * row_size)
75    }}
76}
77
78/// Common color constants accessible by names.
79pub mod consts;
80
81mod decoder;
82pub mod encoder;
83
84#[derive(Clone, Debug, Eq, PartialEq)]
85enum BmpVersion {
86    Version1,
87    Version2,
88    Version3,
89    Version3NT,
90    Version4,
91}
92
93impl AsRef<str> for BmpVersion {
94    fn as_ref(&self) -> &str {
95        match *self {
96            Version1   => "BMP Version 1",
97            Version2   => "BMP Version 2",
98            Version3   => "BMP Version 3",
99            Version3NT => "BMP Version 3 NT",
100            Version4   => "BMP Version 4",
101        }
102    }
103}
104
105#[derive(Clone, Debug, Eq, PartialEq)]
106enum CompressionType {
107    Uncompressed,
108    Rle8bit,
109    Rle4bit,
110    // Only for BMP version 4
111    BitfieldsEncoding,
112}
113
114impl CompressionType {
115    fn from_u32(val: u32) -> CompressionType {
116        match val {
117            1 => Rle8bit,
118            2 => Rle4bit,
119            3 => BitfieldsEncoding,
120            _ => Uncompressed,
121        }
122    }
123}
124
125impl AsRef<str> for CompressionType {
126    fn as_ref(&self) -> &str {
127        match *self {
128            Rle8bit           => "RLE 8-bit",
129            Rle4bit           => "RLE 4-bit",
130            BitfieldsEncoding => "Bitfields Encoding",
131            Uncompressed      => "Uncompressed",
132        }
133    }
134}
135
136#[derive(Clone, Debug, Eq, PartialEq)]
137struct BmpHeader {
138    file_size: u32,
139    creator1: u16,
140    creator2: u16,
141    pixel_offset: u32
142}
143
144impl BmpHeader {
145    pub fn new(header_size: u32, data_size: u32) -> BmpHeader {
146        BmpHeader {
147            file_size: header_size + data_size,
148            creator1: 0 /* Unused */,
149            creator2: 0 /* Unused */,
150            pixel_offset: header_size
151        }
152    }
153}
154
155#[derive(Clone, Debug, Eq, PartialEq)]
156struct BmpDibHeader {
157    header_size: u32,
158    width: i32,
159    height: i32,
160    num_planes: u16,
161    bits_per_pixel: u16,
162    compress_type: u32,
163    data_size: u32,
164    hres: i32,
165    vres: i32,
166    num_colors: u32,
167    num_imp_colors: u32,
168}
169
170impl BmpDibHeader {
171    pub fn new(width: i32, height: i32) -> BmpDibHeader {
172        let row_size = ((24.0 * width as f32 + 31.0) / 32.0).floor() as u32 * 4;
173        let pixel_array_size = row_size * height as u32;
174
175        BmpDibHeader {
176            header_size: 40,
177            width: width,
178            height: height,
179            num_planes: 1,
180            bits_per_pixel: 24,
181            compress_type: 0,
182            data_size: pixel_array_size,
183            hres: 1000,
184            vres: 1000,
185            num_colors: 0,
186            num_imp_colors: 0
187        }
188    }
189}
190
191/// The image type provided by the library.
192///
193/// It exposes functions to initialize or read BMP images from disk, common modification of pixel
194/// data, and saving to disk.
195///
196/// The image is accessed in row-major order from top to bottom,
197/// where point (0, 0) is defined to be in the upper left corner of the image.
198///
199/// Currently, only uncompressed BMP images are supported.
200#[derive(Clone, Eq, PartialEq)]
201pub struct Image {
202    header: BmpHeader,
203    dib_header: BmpDibHeader,
204    color_palette: Option<Vec<Pixel>>,
205    width: u32,
206    height: u32,
207    padding: u32,
208    data: Vec<Pixel>
209}
210
211impl fmt::Debug for Image {
212    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
213        try!(write!(f, "Image {}\n", '{'));
214        try!(write!(f, "\theader: {:?},\n", self.header));
215        try!(write!(f, "\tdib_header: {:?},\n", self.dib_header));
216        try!(write!(f, "\tcolor_palette: {:?},\n", self.color_palette));
217        try!(write!(f, "\twidth: {:?},\n", self.width));
218        try!(write!(f, "\theight: {:?},\n", self.height));
219        try!(write!(f, "\tpadding: {:?},\n", self.padding));
220        write!(f, "{}", '}')
221    }
222}
223
224impl Image {
225    /// Returns a new BMP Image with the `width` and `height` specified. It is initialized to
226    /// a black image by default.
227    ///
228    /// # Example
229    ///
230    /// ```
231    /// extern crate bmp;
232    ///
233    /// let mut img = bmp::Image::new(100, 80);
234    /// ```
235    pub fn new(width: u32, height: u32) -> Image {
236        let mut data = Vec::with_capacity((width * height) as usize);
237        for _ in 0 .. width * height {
238            data.push(px!(0, 0, 0));
239        }
240
241        let (header_size, data_size) = file_size!(24, width, height);
242        Image {
243            header: BmpHeader::new(header_size, data_size),
244            dib_header: BmpDibHeader::new(width as i32, height as i32),
245            color_palette: None,
246            width: width,
247            height: height,
248            padding: width % 4,
249            data: data
250        }
251    }
252
253    /// Returns the `width` of the Image.
254    #[inline]
255    pub fn get_width(&self) -> u32 {
256        self.width
257    }
258
259    /// Returns the `height` of the Image.
260    #[inline]
261    pub fn get_height(&self) -> u32 {
262        self.height
263    }
264
265    /// Set the pixel value at the position of `width` and `height`.
266    ///
267    /// # Example
268    ///
269    /// ```
270    /// extern crate bmp;
271    ///
272    /// let mut img = bmp::Image::new(100, 80);
273    /// img.set_pixel(10, 10, bmp::consts::RED);
274    /// ```
275    #[inline]
276    pub fn set_pixel(&mut self, x: u32, y: u32, val: Pixel) {
277        self.data[((self.height - y - 1) * self.width + x) as usize] = val;
278    }
279
280    /// Returns the pixel value at the position of `width` and `height`.
281    ///
282    /// # Example
283    ///
284    /// ```
285    /// extern crate bmp;
286    ///
287    /// let img = bmp::Image::new(100, 80);
288    /// assert_eq!(bmp::consts::BLACK, img.get_pixel(10, 10));
289    /// ```
290    #[inline]
291    pub fn get_pixel(&self, x: u32, y: u32) -> Pixel {
292        self.data[((self.height - y - 1) * self.width + x) as usize]
293    }
294
295    /// Returns a new `ImageIndex` that iterates over the image dimensions in top-bottom order.
296    ///
297    /// # Example
298    ///
299    /// ```
300    /// extern crate bmp;
301    ///
302    /// let mut img = bmp::Image::new(100, 100);
303    /// for (x, y) in img.coordinates() {
304    ///     img.set_pixel(x, y, bmp::consts::BLUE);
305    /// }
306    /// ```
307    #[inline]
308    pub fn coordinates(&self) -> ImageIndex {
309        ImageIndex::new(self.width as u32, self.height as u32)
310    }
311
312    /// Saves the image to the path specified by `name`. The function will overwrite the contents
313    /// if a file already exists with the same name.
314    ///
315    /// The function returns the `io::Result` from the underlying `Reader`.
316    ///
317    /// # Example
318    ///
319    /// ```
320    /// extern crate bmp;
321    ///
322    /// let mut img = bmp::Image::new(100, 100);
323    /// let _ = img.save("black.bmp").unwrap_or_else(|e| {
324    ///     panic!("Failed to save: {}", e)
325    /// });
326    /// ```
327    pub fn save(&self, name: &str) -> io::Result<()> {
328        // only 24 bpp encoding supported
329        let bmp_data = try!(encoder::encode_image(self));
330        let mut bmp_file = try!(fs::File::create(name));
331        try!(bmp_file.write(&bmp_data));
332        Ok(())
333    }
334}
335
336/// Returns a `BmpResult`, either containing an `Image` or a `BmpError`.
337///
338/// # Example
339///
340/// ```
341/// extern crate bmp;
342///
343/// let img = bmp::open("test/rgbw.bmp").unwrap_or_else(|e| {
344///    panic!("Failed to open: {}", e);
345/// });
346/// ```
347pub fn open(name: &str) -> BmpResult<Image> {
348    let mut bytes = Vec::new();
349    let mut f = try!(fs::File::open(name));
350    try!(f.read_to_end(&mut bytes));
351    let mut bmp_data = Cursor::new(bytes);
352
353    decoder::decode_image(&mut bmp_data)
354}
355
356/// An `Iterator` returning the `x` and `y` coordinates of an image.
357///
358/// It supports iteration over an image in row-major order,
359/// starting from in the upper left corner of the image.
360#[derive(Clone, Copy)]
361pub struct ImageIndex {
362    width: u32,
363    height: u32,
364    x: u32,
365    y: u32
366}
367
368impl ImageIndex {
369    fn new(width: u32, height: u32) -> ImageIndex {
370        ImageIndex {
371            width: width,
372            height: height,
373            x: 0,
374            y: 0
375        }
376    }
377}
378
379impl Iterator for ImageIndex {
380    type Item = (u32, u32);
381
382    fn next(&mut self) -> Option<(u32, u32)> {
383        if self.x < self.width && self.y < self.height {
384            let this = Some((self.x, self.y));
385            self.x += 1;
386            if self.x == self.width {
387                self.x = 0;
388                self.y += 1;
389            }
390            this
391        } else {
392            None
393        }
394    }
395}