summaryrefslogtreecommitdiffstats
path: root/third_party/rust/image/src/bmp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /third_party/rust/image/src/bmp
parentInitial commit. (diff)
downloadfirefox-upstream.tar.xz
firefox-upstream.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/image/src/bmp')
-rw-r--r--third_party/rust/image/src/bmp/decoder.rs1367
-rw-r--r--third_party/rust/image/src/bmp/encoder.rs348
-rw-r--r--third_party/rust/image/src/bmp/mod.rs14
3 files changed, 1729 insertions, 0 deletions
diff --git a/third_party/rust/image/src/bmp/decoder.rs b/third_party/rust/image/src/bmp/decoder.rs
new file mode 100644
index 0000000000..3b32d0810c
--- /dev/null
+++ b/third_party/rust/image/src/bmp/decoder.rs
@@ -0,0 +1,1367 @@
+use std::convert::TryFrom;
+use std::io::{self, Cursor, Read, Seek, SeekFrom};
+use std::iter::{repeat, Iterator, Rev};
+use std::marker::PhantomData;
+use std::slice::ChunksMut;
+use std::{cmp, mem};
+use std::cmp::Ordering;
+
+use byteorder::{LittleEndian, ReadBytesExt};
+
+use crate::color::ColorType;
+use crate::error::{ImageError, ImageResult};
+use crate::image::{self, ImageDecoder, ImageDecoderExt, Progress};
+
+const BITMAPCOREHEADER_SIZE: u32 = 12;
+const BITMAPINFOHEADER_SIZE: u32 = 40;
+const BITMAPV2HEADER_SIZE: u32 = 52;
+const BITMAPV3HEADER_SIZE: u32 = 56;
+const BITMAPV4HEADER_SIZE: u32 = 108;
+const BITMAPV5HEADER_SIZE: u32 = 124;
+
+static LOOKUP_TABLE_3_BIT_TO_8_BIT: [u8; 8] = [0, 36, 73, 109, 146, 182, 219, 255];
+static LOOKUP_TABLE_4_BIT_TO_8_BIT: [u8; 16] = [
+ 0, 17, 34, 51, 68, 85, 102, 119, 136, 153, 170, 187, 204, 221, 238, 255,
+];
+static LOOKUP_TABLE_5_BIT_TO_8_BIT: [u8; 32] = [
+ 0, 8, 16, 25, 33, 41, 49, 58, 66, 74, 82, 90, 99, 107, 115, 123, 132, 140, 148, 156, 165, 173,
+ 181, 189, 197, 206, 214, 222, 230, 239, 247, 255,
+];
+static LOOKUP_TABLE_6_BIT_TO_8_BIT: [u8; 64] = [
+ 0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 45, 49, 53, 57, 61, 65, 69, 73, 77, 81, 85, 89, 93,
+ 97, 101, 105, 109, 113, 117, 121, 125, 130, 134, 138, 142, 146, 150, 154, 158, 162, 166, 170,
+ 174, 178, 182, 186, 190, 194, 198, 202, 206, 210, 215, 219, 223, 227, 231, 235, 239, 243, 247,
+ 251, 255,
+];
+
+static R5_G5_B5_COLOR_MASK: Bitfields = Bitfields {
+ r: Bitfield { len: 5, shift: 10 },
+ g: Bitfield { len: 5, shift: 5 },
+ b: Bitfield { len: 5, shift: 0 },
+ a: Bitfield { len: 0, shift: 0 },
+};
+const R8_G8_B8_COLOR_MASK: Bitfields = Bitfields {
+ r: Bitfield { len: 8, shift: 24 },
+ g: Bitfield { len: 8, shift: 16 },
+ b: Bitfield { len: 8, shift: 8 },
+ a: Bitfield { len: 0, shift: 0 },
+};
+
+const RLE_ESCAPE: u8 = 0;
+const RLE_ESCAPE_EOL: u8 = 0;
+const RLE_ESCAPE_EOF: u8 = 1;
+const RLE_ESCAPE_DELTA: u8 = 2;
+
+/// The maximum width/height the decoder will process.
+const MAX_WIDTH_HEIGHT: i32 = 0xFFFF;
+
+#[derive(PartialEq, Copy, Clone)]
+enum ImageType {
+ Palette,
+ RGB16,
+ RGB24,
+ RGB32,
+ RGBA32,
+ RLE8,
+ RLE4,
+ Bitfields16,
+ Bitfields32,
+}
+
+#[derive(PartialEq)]
+enum BMPHeaderType {
+ Core,
+ Info,
+ V2,
+ V3,
+ V4,
+ V5,
+}
+
+#[derive(PartialEq)]
+enum FormatFullBytes {
+ RGB24,
+ RGB32,
+ RGBA32,
+ Format888,
+}
+
+enum Chunker<'a> {
+ FromTop(ChunksMut<'a, u8>),
+ FromBottom(Rev<ChunksMut<'a, u8>>),
+}
+
+pub(crate) struct RowIterator<'a> {
+ chunks: Chunker<'a>,
+}
+
+impl<'a> Iterator for RowIterator<'a> {
+ type Item = &'a mut [u8];
+
+ #[inline(always)]
+ fn next(&mut self) -> Option<&'a mut [u8]> {
+ match self.chunks {
+ Chunker::FromTop(ref mut chunks) => chunks.next(),
+ Chunker::FromBottom(ref mut chunks) => chunks.next(),
+ }
+ }
+}
+
+/// Convenience function to check if the combination of width, length and number of
+/// channels would result in a buffer that would overflow.
+fn check_for_overflow(width: i32, length: i32, channels: usize) -> ImageResult<()> {
+ num_bytes(width, length, channels)
+ .map(|_| ())
+ .ok_or_else(|| {
+ ImageError::FormatError(
+ "Image would require a buffer that is too large to be represented!".to_owned(),
+ )
+ })
+}
+
+/// Calculate how many many bytes a buffer holding a decoded image with these properties would
+/// require. Returns `None` if the buffer size would overflow or if one of the sizes are negative.
+fn num_bytes(width: i32, length: i32, channels: usize) -> Option<usize> {
+ if width <= 0 || length <= 0 {
+ None
+ } else {
+ match channels.checked_mul(width as usize) {
+ Some(n) => n.checked_mul(length as usize),
+ None => None,
+ }
+ }
+}
+
+/// The maximum starting number of pixels in the pixel buffer, might want to tweak this.
+///
+/// For images that specify large sizes, we don't allocate the full buffer right away
+/// to somewhat mitigate trying to make the decoder run out of memory by sending a bogus image.
+/// This is somewhat of a workaroud as ideally we would check against the expected file size
+/// but that's not possible through the Read and Seek traits alone and would require the encoder
+/// to provided with it from the caller.
+///
+/// NOTE: This is multiplied by 3 or 4 depending on the number of channels to get the maximum
+/// starting buffer size. This amounts to about 134 mb for a buffer with 4 channels.
+const MAX_INITIAL_PIXELS: usize = 8192 * 4096;
+
+/// Sets all bytes in an mutable iterator over slices of bytes to 0.
+fn blank_bytes<'a, T: Iterator<Item = &'a mut [u8]>>(iterator: T) {
+ for chunk in iterator {
+ for b in chunk {
+ *b = 0;
+ }
+ }
+}
+
+/// Extend the buffer to `full_size`, copying existing data to the end of the buffer. Returns slice
+/// pointing to the part of the buffer that is not yet filled in.
+///
+/// If blank is true, the bytes in the new buffer that are not filled in are set to 0.
+/// This is used for rle-encoded images as the decoding process for these may not fill in all the
+/// pixels.
+///
+/// As BMP images are usually stored with the rows upside-down we have to write the image data
+/// starting at the end of the buffer and thus we have to make sure the existing data is put at the
+/// end of the buffer.
+#[inline(never)]
+#[cold]
+fn extend_buffer(buffer: &mut Vec<u8>, full_size: usize, blank: bool) -> &mut [u8] {
+ let old_size = buffer.len();
+ let extend = full_size - buffer.len();
+
+ buffer.extend(repeat(0xFF).take(extend));
+ assert_eq!(buffer.len(), full_size);
+
+ let ret = if extend >= old_size {
+ // If the full buffer length is more or equal to twice the initial one, we can simply
+ // copy the data in the lower part of the buffer to the end of it and input from there.
+ let (new, old) = buffer.split_at_mut(extend);
+ old.copy_from_slice(&new[..old_size]);
+ new
+ } else {
+ // If the full size is less than twice the initial buffer, we have to
+ // copy in two steps
+ let overlap = old_size - extend;
+
+ // First we copy the data that fits into the bit we extended.
+ let (lower, upper) = buffer.split_at_mut(old_size);
+ upper.copy_from_slice(&lower[overlap..]);
+
+ // Then we slide the data that hasn't been copied yet to the top of the buffer
+ let (new, old) = lower.split_at_mut(extend);
+ old[..overlap].copy_from_slice(&new[..overlap]);
+ new
+ };
+ if blank {
+ for b in ret.iter_mut() {
+ *b = 0;
+ }
+ };
+ ret
+}
+
+/// Call the provided function on each row of the provided buffer, returning Err if the provided
+/// function returns an error, extends the buffer if it's not large enough.
+fn with_rows<F>(
+ buffer: &mut Vec<u8>,
+ width: i32,
+ height: i32,
+ channels: usize,
+ top_down: bool,
+ mut func: F,
+) -> io::Result<()>
+where
+ F: FnMut(&mut [u8]) -> io::Result<()>,
+{
+ // An overflow should already have been checked for when this is called,
+ // though we check anyhow, as it somehow seems to increase performance slightly.
+ let row_width = channels.checked_mul(width as usize).unwrap();
+ let full_image_size = row_width.checked_mul(height as usize).unwrap();
+
+ if !top_down {
+ for row in buffer.chunks_mut(row_width).rev() {
+ func(row)?;
+ }
+
+ // If we need more space, extend the buffer.
+ if buffer.len() < full_image_size {
+ let new_space = extend_buffer(buffer, full_image_size, false);
+ for row in new_space.chunks_mut(row_width).rev() {
+ func(row)?;
+ }
+ }
+ } else {
+ for row in buffer.chunks_mut(row_width) {
+ func(row)?;
+ }
+ if buffer.len() < full_image_size {
+ // If the image is stored in top-down order, we can simply use the extend function
+ // from vec to extend the buffer..
+ let extend = full_image_size - buffer.len();
+ buffer.extend(repeat(0xFF).take(extend));
+ let len = buffer.len();
+ for row in buffer[len - row_width..].chunks_mut(row_width) {
+ func(row)?;
+ }
+ };
+ }
+ Ok(())
+}
+
+fn set_8bit_pixel_run<'a, T: Iterator<Item = &'a u8>>(
+ pixel_iter: &mut ChunksMut<u8>,
+ palette: &[(u8, u8, u8)],
+ indices: T,
+ n_pixels: usize,
+) -> bool {
+ for idx in indices.take(n_pixels) {
+ if let Some(pixel) = pixel_iter.next() {
+ let (r, g, b) = palette[*idx as usize];
+ pixel[0] = r;
+ pixel[1] = g;
+ pixel[2] = b;
+ } else {
+ return false;
+ }
+ }
+ true
+}
+
+fn set_4bit_pixel_run<'a, T: Iterator<Item = &'a u8>>(
+ pixel_iter: &mut ChunksMut<u8>,
+ palette: &[(u8, u8, u8)],
+ indices: T,
+ mut n_pixels: usize,
+) -> bool {
+ for idx in indices {
+ macro_rules! set_pixel {
+ ($i:expr) => {
+ if n_pixels == 0 {
+ break;
+ }
+ if let Some(pixel) = pixel_iter.next() {
+ let (r, g, b) = palette[$i as usize];
+ pixel[0] = r;
+ pixel[1] = g;
+ pixel[2] = b;
+ } else {
+ return false;
+ }
+ n_pixels -= 1;
+ };
+ }
+ set_pixel!(idx >> 4);
+ set_pixel!(idx & 0xf);
+ }
+ true
+}
+
+#[rustfmt::skip]
+fn set_2bit_pixel_run<'a, T: Iterator<Item = &'a u8>>(
+ pixel_iter: &mut ChunksMut<u8>,
+ palette: &[(u8, u8, u8)],
+ indices: T,
+ mut n_pixels: usize,
+) -> bool {
+ for idx in indices {
+ macro_rules! set_pixel {
+ ($i:expr) => {
+ if n_pixels == 0 {
+ break;
+ }
+ if let Some(pixel) = pixel_iter.next() {
+ let (r, g, b) = palette[$i as usize];
+ pixel[0] = r;
+ pixel[1] = g;
+ pixel[2] = b;
+ } else {
+ return false;
+ }
+ n_pixels -= 1;
+ };
+ }
+ set_pixel!((idx >> 6) & 0x3u8);
+ set_pixel!((idx >> 4) & 0x3u8);
+ set_pixel!((idx >> 2) & 0x3u8);
+ set_pixel!( idx & 0x3u8);
+ }
+ true
+}
+
+fn set_1bit_pixel_run<'a, T: Iterator<Item = &'a u8>>(
+ pixel_iter: &mut ChunksMut<u8>,
+ palette: &[(u8, u8, u8)],
+ indices: T,
+) {
+ for idx in indices {
+ let mut bit = 0x80;
+ loop {
+ if let Some(pixel) = pixel_iter.next() {
+ let (r, g, b) = palette[((idx & bit) != 0) as usize];
+ pixel[0] = r;
+ pixel[1] = g;
+ pixel[2] = b;
+ } else {
+ return;
+ }
+
+ bit >>= 1;
+ if bit == 0 {
+ break;
+ }
+ }
+ }
+}
+
+#[derive(PartialEq, Eq)]
+struct Bitfield {
+ shift: u32,
+ len: u32,
+}
+
+impl Bitfield {
+ fn from_mask(mask: u32, max_len: u32) -> ImageResult<Bitfield> {
+ if mask == 0 {
+ return Ok(Bitfield { shift: 0, len: 0 });
+ }
+ let mut shift = mask.trailing_zeros();
+ let mut len = (!(mask >> shift)).trailing_zeros();
+ if len != mask.count_ones() {
+ return Err(ImageError::FormatError(
+ "Non-contiguous bitfield mask".to_string(),
+ ));
+ }
+ if len + shift > max_len {
+ return Err(ImageError::FormatError("Invalid bitfield mask".to_string()));
+ }
+ if len > 8 {
+ shift += len - 8;
+ len = 8;
+ }
+ Ok(Bitfield { shift, len })
+ }
+
+ fn read(&self, data: u32) -> u8 {
+ let data = data >> self.shift;
+ match self.len {
+ 1 => ((data & 0b1) * 0xff) as u8,
+ 2 => ((data & 0b11) * 0x55) as u8,
+ 3 => LOOKUP_TABLE_3_BIT_TO_8_BIT[(data & 0b00_0111) as usize],
+ 4 => LOOKUP_TABLE_4_BIT_TO_8_BIT[(data & 0b00_1111) as usize],
+ 5 => LOOKUP_TABLE_5_BIT_TO_8_BIT[(data & 0b01_1111) as usize],
+ 6 => LOOKUP_TABLE_6_BIT_TO_8_BIT[(data & 0b11_1111) as usize],
+ 7 => ((data & 0x7f) << 1 | (data & 0x7f) >> 6) as u8,
+ 8 => (data & 0xff) as u8,
+ _ => panic!(),
+ }
+ }
+}
+
+#[derive(PartialEq, Eq)]
+struct Bitfields {
+ r: Bitfield,
+ g: Bitfield,
+ b: Bitfield,
+ a: Bitfield,
+}
+
+impl Bitfields {
+ fn from_mask(
+ r_mask: u32,
+ g_mask: u32,
+ b_mask: u32,
+ a_mask: u32,
+ max_len: u32,
+ ) -> ImageResult<Bitfields> {
+ let bitfields = Bitfields {
+ r: Bitfield::from_mask(r_mask, max_len)?,
+ g: Bitfield::from_mask(g_mask, max_len)?,
+ b: Bitfield::from_mask(b_mask, max_len)?,
+ a: Bitfield::from_mask(a_mask, max_len)?,
+ };
+ if bitfields.r.len == 0 || bitfields.g.len == 0 || bitfields.b.len == 0 {
+ return Err(ImageError::FormatError("Missing bitfield mask".to_string()));
+ }
+ Ok(bitfields)
+ }
+}
+
+/// A bmp decoder
+pub struct BmpDecoder<R> {
+ reader: R,
+
+ bmp_header_type: BMPHeaderType,
+
+ width: i32,
+ height: i32,
+ data_offset: u64,
+ top_down: bool,
+ no_file_header: bool,
+ add_alpha_channel: bool,
+ has_loaded_metadata: bool,
+ image_type: ImageType,
+
+ bit_count: u16,
+ colors_used: u32,
+ palette: Option<Vec<(u8, u8, u8)>>,
+ bitfields: Option<Bitfields>,
+}
+
+enum RLEInsn {
+ EndOfFile,
+ EndOfRow,
+ Delta(u8, u8),
+ Absolute(u8, Vec<u8>),
+ PixelRun(u8, u8),
+}
+
+struct RLEInsnIterator<'a, R: 'a + Read> {
+ r: &'a mut R,
+ image_type: ImageType,
+}
+
+impl<'a, R: Read> Iterator for RLEInsnIterator<'a, R> {
+ type Item = RLEInsn;
+
+ fn next(&mut self) -> Option<RLEInsn> {
+ let control_byte = match self.r.read_u8() {
+ Ok(b) => b,
+ Err(_) => return None,
+ };
+
+ match control_byte {
+ RLE_ESCAPE => {
+ let op = match self.r.read_u8() {
+ Ok(b) => b,
+ Err(_) => return None,
+ };
+
+ match op {
+ RLE_ESCAPE_EOL => Some(RLEInsn::EndOfRow),
+ RLE_ESCAPE_EOF => Some(RLEInsn::EndOfFile),
+ RLE_ESCAPE_DELTA => {
+ let xdelta = match self.r.read_u8() {
+ Ok(n) => n,
+ Err(_) => return None,
+ };
+ let ydelta = match self.r.read_u8() {
+ Ok(n) => n,
+ Err(_) => return None,
+ };
+ Some(RLEInsn::Delta(xdelta, ydelta))
+ }
+ _ => {
+ let mut length = op as usize;
+ if self.image_type == ImageType::RLE4 {
+ length = (length + 1) / 2;
+ }
+ length += length & 1;
+ let mut buffer = vec![0; length];
+ match self.r.read_exact(&mut buffer) {
+ Ok(()) => Some(RLEInsn::Absolute(op, buffer)),
+ Err(_) => None,
+ }
+ }
+ }
+ }
+ _ => match self.r.read_u8() {
+ Ok(palette_index) => Some(RLEInsn::PixelRun(control_byte, palette_index)),
+ Err(_) => None,
+ },
+ }
+ }
+}
+
+impl<R: Read + Seek> BmpDecoder<R> {
+ /// Create a new decoder that decodes from the stream ```r```
+ pub fn new(reader: R) -> ImageResult<BmpDecoder<R>> {
+ let mut decoder = BmpDecoder {
+ reader,
+
+ bmp_header_type: BMPHeaderType::Info,
+
+ width: 0,
+ height: 0,
+ data_offset: 0,
+ top_down: false,
+ no_file_header: false,
+ add_alpha_channel: false,
+ has_loaded_metadata: false,
+ image_type: ImageType::Palette,
+
+ bit_count: 0,
+ colors_used: 0,
+ palette: None,
+ bitfields: None,
+ };
+
+ decoder.read_metadata()?;
+ Ok(decoder)
+ }
+
+ #[cfg(feature = "ico")]
+ pub(crate) fn new_with_ico_format(reader: R) -> ImageResult<BmpDecoder<R>> {
+ let mut decoder = BmpDecoder {
+ reader,
+
+ bmp_header_type: BMPHeaderType::Info,
+
+ width: 0,
+ height: 0,
+ data_offset: 0,
+ top_down: false,
+ no_file_header: false,
+ add_alpha_channel: false,
+ has_loaded_metadata: false,
+ image_type: ImageType::Palette,
+
+ bit_count: 0,
+ colors_used: 0,
+ palette: None,
+ bitfields: None,
+ };
+
+ decoder.read_metadata_in_ico_format()?;
+ Ok(decoder)
+ }
+
+ #[cfg(feature = "ico")]
+ pub(crate) fn reader(&mut self) -> &mut R {
+ &mut self.reader
+ }
+
+ fn read_file_header(&mut self) -> ImageResult<()> {
+ if self.no_file_header {
+ return Ok(());
+ }
+ let mut signature = [0; 2];
+ self.reader.read_exact(&mut signature)?;
+
+ if signature != b"BM"[..] {
+ return Err(ImageError::FormatError(
+ "BMP signature not found".to_string(),
+ ));
+ }
+
+ // The next 8 bytes represent file size, followed the 4 reserved bytes
+ // We're not interesting these values
+ self.reader.read_u32::<LittleEndian>()?;
+ self.reader.read_u32::<LittleEndian>()?;
+
+ self.data_offset = u64::from(self.reader.read_u32::<LittleEndian>()?);
+
+ Ok(())
+ }
+
+ /// Read BITMAPCOREHEADER https://msdn.microsoft.com/en-us/library/vs/alm/dd183372(v=vs.85).aspx
+ ///
+ /// returns Err if any of the values are invalid.
+ fn read_bitmap_core_header(&mut self) -> ImageResult<()> {
+ // As height/width values in BMP files with core headers are only 16 bits long,
+ // they won't be larger than `MAX_WIDTH_HEIGHT`.
+ self.width = i32::from(self.reader.read_u16::<LittleEndian>()?);
+ self.height = i32::from(self.reader.read_u16::<LittleEndian>()?);
+
+ check_for_overflow(self.width, self.height, self.num_channels())?;
+
+ // Number of planes (format specifies that this should be 1).
+ if self.reader.read_u16::<LittleEndian>()? != 1 {
+ return Err(ImageError::FormatError("More than one plane".to_string()));
+ }
+
+ self.bit_count = self.reader.read_u16::<LittleEndian>()?;
+ self.image_type = match self.bit_count {
+ 1 | 4 | 8 => ImageType::Palette,
+ 24 => ImageType::RGB24,
+ _ => return Err(ImageError::FormatError("Invalid bit count".to_string())),
+ };
+
+ Ok(())
+ }
+
+ /// Read BITMAPINFOHEADER https://msdn.microsoft.com/en-us/library/vs/alm/dd183376(v=vs.85).aspx
+ /// or BITMAPV{2|3|4|5}HEADER.
+ ///
+ /// returns Err if any of the values are invalid.
+ fn read_bitmap_info_header(&mut self) -> ImageResult<()> {
+ self.width = self.reader.read_i32::<LittleEndian>()?;
+ self.height = self.reader.read_i32::<LittleEndian>()?;
+
+ // Width can not be negative
+ if self.width < 0 {
+ return Err(ImageError::FormatError("Negative width".to_string()));
+ } else if self.width > MAX_WIDTH_HEIGHT || self.height > MAX_WIDTH_HEIGHT {
+ // Limit very large image sizes to avoid OOM issues. Images with these sizes are
+ // unlikely to be valid anyhow.
+ return Err(ImageError::FormatError("Image too large".to_string()));
+ }
+
+ if self.height == i32::min_value() {
+ return Err(ImageError::FormatError("Invalid height".to_string()));
+ }
+
+ // A negative height indicates a top-down DIB.
+ if self.height < 0 {
+ self.height *= -1;
+ self.top_down = true;
+ }
+
+ check_for_overflow(self.width, self.height, self.num_channels())?;
+
+ // Number of planes (format specifies that this should be 1).
+ if self.reader.read_u16::<LittleEndian>()? != 1 {
+ return Err(ImageError::FormatError("More than one plane".to_string()));
+ }
+
+ self.bit_count = self.reader.read_u16::<LittleEndian>()?;
+ let image_type_u32 = self.reader.read_u32::<LittleEndian>()?;
+
+ // Top-down dibs can not be compressed.
+ if self.top_down && image_type_u32 != 0 && image_type_u32 != 3 {
+ return Err(ImageError::FormatError(
+ "Invalid image type for top-down image.".to_string(),
+ ));
+ }
+ self.image_type = match image_type_u32 {
+ 0 => match self.bit_count {
+ 1 | 2 | 4 | 8 => ImageType::Palette,
+ 16 => ImageType::RGB16,
+ 24 => ImageType::RGB24,
+ 32 if self.add_alpha_channel => ImageType::RGBA32,
+ 32 => ImageType::RGB32,
+ _ => {
+ return Err(ImageError::FormatError(format!(
+ "Invalid RGB bit count {}",
+ self.bit_count
+ )))
+ }
+ },
+ 1 => match self.bit_count {
+ 8 => ImageType::RLE8,
+ _ => {
+ return Err(ImageError::FormatError(
+ "Invalid RLE8 bit count".to_string(),
+ ))
+ }
+ },
+ 2 => match self.bit_count {
+ 4 => ImageType::RLE4,
+ _ => {
+ return Err(ImageError::FormatError(
+ "Invalid RLE4 bit count".to_string(),
+ ))
+ }
+ },
+ 3 => match self.bit_count {
+ 16 => ImageType::Bitfields16,
+ 32 => ImageType::Bitfields32,
+ _ => {
+ return Err(ImageError::FormatError(
+ "Invalid bitfields bit count".to_string(),
+ ))
+ }
+ },
+ // PNG and JPEG not implemented yet.
+ _ => {
+ return Err(ImageError::UnsupportedError(
+ "Unsupported image type".to_string(),
+ ))
+ }
+ };
+
+ // The next 12 bytes represent data array size in bytes,
+ // followed the horizontal and vertical printing resolutions
+ // We will calculate the pixel array size using width & height of image
+ // We're not interesting the horz or vert printing resolutions
+ self.reader.read_u32::<LittleEndian>()?;
+ self.reader.read_u32::<LittleEndian>()?;
+ self.reader.read_u32::<LittleEndian>()?;
+
+ self.colors_used = self.reader.read_u32::<LittleEndian>()?;
+
+ // The next 4 bytes represent number of "important" colors
+ // We're not interested in this value, so we'll skip it
+ self.reader.read_u32::<LittleEndian>()?;
+
+ Ok(())
+ }
+
+ fn read_bitmasks(&mut self) -> ImageResult<()> {
+ let r_mask = self.reader.read_u32::<LittleEndian>()?;
+ let g_mask = self.reader.read_u32::<LittleEndian>()?;
+ let b_mask = self.reader.read_u32::<LittleEndian>()?;
+
+ let a_mask = match self.bmp_header_type {
+ BMPHeaderType::V3 | BMPHeaderType::V4 | BMPHeaderType::V5 => {
+ self.reader.read_u32::<LittleEndian>()?
+ }
+ _ => 0,
+ };
+
+ self.bitfields = match self.image_type {
+ ImageType::Bitfields16 => {
+ Some(Bitfields::from_mask(r_mask, g_mask, b_mask, a_mask, 16)?)
+ }
+ ImageType::Bitfields32 => {
+ Some(Bitfields::from_mask(r_mask, g_mask, b_mask, a_mask, 32)?)
+ }
+ _ => None,
+ };
+
+ if self.bitfields.is_some() && a_mask != 0 {
+ self.add_alpha_channel = true;
+ }
+
+ Ok(())
+ }
+
+ fn read_metadata(&mut self) -> ImageResult<()> {
+ if !self.has_loaded_metadata {
+ self.read_file_header()?;
+ let bmp_header_offset = self.reader.seek(SeekFrom::Current(0))?;
+ let bmp_header_size = self.reader.read_u32::<LittleEndian>()?;
+ let bmp_header_end = bmp_header_offset + u64::from(bmp_header_size);
+
+ self.bmp_header_type = match bmp_header_size {
+ BITMAPCOREHEADER_SIZE => BMPHeaderType::Core,
+ BITMAPINFOHEADER_SIZE => BMPHeaderType::Info,
+ BITMAPV2HEADER_SIZE => BMPHeaderType::V2,
+ BITMAPV3HEADER_SIZE => BMPHeaderType::V3,
+ BITMAPV4HEADER_SIZE => BMPHeaderType::V4,
+ BITMAPV5HEADER_SIZE => BMPHeaderType::V5,
+ _ => {
+ return Err(ImageError::UnsupportedError(
+ "Unsupported Bitmap Header".to_string(),
+ ))
+ }
+ };
+
+ match self.bmp_header_type {
+ BMPHeaderType::Core => {
+ self.read_bitmap_core_header()?;
+ }
+ BMPHeaderType::Info
+ | BMPHeaderType::V2
+ | BMPHeaderType::V3
+ | BMPHeaderType::V4
+ | BMPHeaderType::V5 => {
+ self.read_bitmap_info_header()?;
+ }
+ };
+
+ match self.image_type {
+ ImageType::Bitfields16 | ImageType::Bitfields32 => self.read_bitmasks()?,
+ _ => {}
+ };
+
+ self.reader.seek(SeekFrom::Start(bmp_header_end))?;
+
+ match self.image_type {
+ ImageType::Palette | ImageType::RLE4 | ImageType::RLE8 => self.read_palette()?,
+ _ => {}
+ };
+
+ if self.no_file_header {
+ // Use the offset of the end of metadata instead of reading a BMP file header.
+ self.data_offset = self.reader.seek(SeekFrom::Current(0))?;
+ }
+
+ self.has_loaded_metadata = true;
+ }
+ Ok(())
+ }
+
+ #[cfg(feature = "ico")]
+ #[doc(hidden)]
+ pub fn read_metadata_in_ico_format(&mut self) -> ImageResult<()> {
+ self.no_file_header = true;
+ self.add_alpha_channel = true;
+ self.read_metadata()?;
+
+ // The height field in an ICO file is doubled to account for the AND mask
+ // (whether or not an AND mask is actually present).
+ self.height /= 2;
+ Ok(())
+ }
+
+ fn get_palette_size(&mut self) -> ImageResult<usize> {
+ match self.colors_used {
+ 0 => Ok(1 << self.bit_count),
+ _ => {
+ if self.colors_used > 1 << self.bit_count {
+ return Err(ImageError::FormatError(format!(
+ "Palette size {} exceeds maximum size for BMP with bit count of {}",
+ self.colors_used, self.bit_count
+ )));
+ }
+ Ok(self.colors_used as usize)
+ }
+ }
+ }
+
+ fn bytes_per_color(&self) -> usize {
+ match self.bmp_header_type {
+ BMPHeaderType::Core => 3,
+ _ => 4,
+ }
+ }
+
+ fn read_palette(&mut self) -> ImageResult<()> {
+ const MAX_PALETTE_SIZE: usize = 256; // Palette indices are u8.
+
+ let bytes_per_color = self.bytes_per_color();
+ let palette_size = self.get_palette_size()?;
+ let max_length = MAX_PALETTE_SIZE * bytes_per_color;
+
+ let length = palette_size * bytes_per_color;
+ let mut buf = Vec::with_capacity(max_length);
+
+ // Resize and read the palette entries to the buffer.
+ // We limit the buffer to at most 256 colours to avoid any oom issues as
+ // 8-bit images can't reference more than 256 indexes anyhow.
+ buf.resize(cmp::min(length, max_length), 0);
+ self.reader.by_ref().read_exact(&mut buf)?;
+
+ // Allocate 256 entries even if palette_size is smaller, to prevent corrupt files from
+ // causing an out-of-bounds array access.
+ match length.cmp(&max_length) {
+ Ordering::Greater => {
+ self.reader
+ .seek(SeekFrom::Current((length - max_length) as i64))?;
+ }
+ Ordering::Less => buf.resize(max_length, 0),
+ Ordering::Equal => (),
+ }
+
+ let p: Vec<(u8, u8, u8)> = (0..MAX_PALETTE_SIZE)
+ .map(|i| {
+ let b = buf[bytes_per_color * i];
+ let g = buf[bytes_per_color * i + 1];
+ let r = buf[bytes_per_color * i + 2];
+ (r, g, b)
+ })
+ .collect();
+
+ self.palette = Some(p);
+
+ Ok(())
+ }
+
+ fn num_channels(&self) -> usize {
+ if self.add_alpha_channel {
+ 4
+ } else {
+ 3
+ }
+ }
+
+ /// Create a buffer to hold the decoded pixel data.
+ ///
+ /// The buffer will be large enough to hold the whole image if it requires less than
+ /// `MAX_INITIAL_PIXELS` times the number of channels bytes (adjusted to line up with the
+ /// width of a row).
+ fn create_pixel_data(&self) -> Vec<u8> {
+ let row_width = self.num_channels() * self.width as usize;
+ let max_pixels = self.num_channels() * MAX_INITIAL_PIXELS;
+ // Make sure the maximum size is whole number of rows.
+ let max_starting_size = max_pixels + row_width - (max_pixels % row_width);
+ // The buffer has its bytes initially set to 0xFF as the ICO decoder relies on it.
+ vec![0xFF; cmp::min(row_width * self.height as usize, max_starting_size)]
+ }
+
+ fn rows<'a>(&self, pixel_data: &'a mut [u8]) -> RowIterator<'a> {
+ let stride = self.width as usize * self.num_channels();
+ if self.top_down {
+ RowIterator {
+ chunks: Chunker::FromTop(pixel_data.chunks_mut(stride)),
+ }
+ } else {
+ RowIterator {
+ chunks: Chunker::FromBottom(pixel_data.chunks_mut(stride).rev()),
+ }
+ }
+ }
+
+ fn read_palettized_pixel_data(&mut self) -> ImageResult<Vec<u8>> {
+ let mut pixel_data = self.create_pixel_data();
+ let num_channels = self.num_channels();
+ let row_byte_length = ((i32::from(self.bit_count) * self.width + 31) / 32 * 4) as usize;
+ let mut indices = vec![0; row_byte_length];
+ let palette = self.palette.as_ref().unwrap();
+ let bit_count = self.bit_count;
+ let reader = &mut self.reader;
+ let width = self.width as usize;
+
+ reader.seek(SeekFrom::Start(self.data_offset))?;
+
+ with_rows(
+ &mut pixel_data,
+ self.width,
+ self.height,
+ num_channels,
+ self.top_down,
+ |row| {
+ reader.read_exact(&mut indices)?;
+ let mut pixel_iter = row.chunks_mut(num_channels);
+ match bit_count {
+ 1 => {
+ set_1bit_pixel_run(&mut pixel_iter, palette, indices.iter());
+ }
+ 2 => {
+ set_2bit_pixel_run(&mut pixel_iter, palette, indices.iter(), width);
+ }
+ 4 => {
+ set_4bit_pixel_run(&mut pixel_iter, palette, indices.iter(), width);
+ }
+ 8 => {
+ set_8bit_pixel_run(&mut pixel_iter, palette, indices.iter(), width);
+ }
+ _ => panic!(),
+ };
+ Ok(())
+ },
+ )?;
+
+ Ok(pixel_data)
+ }
+
+ fn read_16_bit_pixel_data(&mut self, bitfields: Option<&Bitfields>) -> ImageResult<Vec<u8>> {
+ let mut pixel_data = self.create_pixel_data();
+ let num_channels = self.num_channels();
+ let row_padding_len = self.width as usize % 2 * 2;
+ let row_padding = &mut [0; 2][..row_padding_len];
+ let bitfields = match bitfields {
+ Some(b) => b,
+ None => self.bitfields.as_ref().unwrap(),
+ };
+ let reader = &mut self.reader;
+
+ reader.seek(SeekFrom::Start(self.data_offset))?;
+
+ with_rows(
+ &mut pixel_data,
+ self.width,
+ self.height,
+ num_channels,
+ self.top_down,
+ |row| {
+ for pixel in row.chunks_mut(num_channels) {
+ let data = u32::from(reader.read_u16::<LittleEndian>()?);
+
+ pixel[0] = bitfields.r.read(data);
+ pixel[1] = bitfields.g.read(data);
+ pixel[2] = bitfields.b.read(data);
+ if num_channels == 4 {
+ pixel[3] = bitfields.a.read(data);
+ }
+ }
+ reader.read_exact(row_padding)
+ },
+ )?;
+
+ Ok(pixel_data)
+ }
+
+ /// Read image data from a reader in 32-bit formats that use bitfields.
+ fn read_32_bit_pixel_data(&mut self) -> ImageResult<Vec<u8>> {
+ let mut pixel_data = self.create_pixel_data();
+ let num_channels = self.num_channels();
+
+ let bitfields = self.bitfields.as_ref().unwrap();
+
+ let reader = &mut self.reader;
+ reader.seek(SeekFrom::Start(self.data_offset))?;
+
+ with_rows(
+ &mut pixel_data,
+ self.width,
+ self.height,
+ num_channels,
+ self.top_down,
+ |row| {
+ for pixel in row.chunks_mut(num_channels) {
+ let data = reader.read_u32::<LittleEndian>()?;
+
+ pixel[0] = bitfields.r.read(data);
+ pixel[1] = bitfields.g.read(data);
+ pixel[2] = bitfields.b.read(data);
+ if num_channels == 4 {
+ pixel[3] = bitfields.a.read(data);
+ }
+ }
+ Ok(())
+ },
+ )?;
+
+ Ok(pixel_data)
+ }
+
+ /// Read image data from a reader where the colours are stored as 8-bit values (24 or 32-bit).
+ fn read_full_byte_pixel_data(&mut self, format: &FormatFullBytes) -> ImageResult<Vec<u8>> {
+ let mut pixel_data = self.create_pixel_data();
+ let num_channels = self.num_channels();
+ let row_padding_len = match *format {
+ FormatFullBytes::RGB24 => (4 - (self.width as usize * 3) % 4) % 4,
+ _ => 0,
+ };
+ let row_padding = &mut [0; 4][..row_padding_len];
+
+ self.reader.seek(SeekFrom::Start(self.data_offset))?;
+
+ let reader = &mut self.reader;
+
+ with_rows(
+ &mut pixel_data,
+ self.width,
+ self.height,
+ num_channels,
+ self.top_down,
+ |row| {
+ for pixel in row.chunks_mut(num_channels) {
+ if *format == FormatFullBytes::Format888 {
+ reader.read_u8()?;
+ }
+
+ // Read the colour values (b, g, r).
+ // Reading 3 bytes and reversing them is significantly faster than reading one
+ // at a time.
+ reader.read_exact(&mut pixel[0..3])?;
+ pixel[0..3].reverse();
+
+ if *format == FormatFullBytes::RGB32 {
+ reader.read_u8()?;
+ }
+
+ // Read the alpha channel if present
+ if *format == FormatFullBytes::RGBA32 {
+ reader.read_exact(&mut pixel[3..4])?;
+ }
+ }
+ reader.read_exact(row_padding)
+ },
+ )?;
+
+ Ok(pixel_data)
+ }
+
+ fn read_rle_data(&mut self, image_type: ImageType) -> ImageResult<Vec<u8>> {
+ // Seek to the start of the actual image data.
+ self.reader.seek(SeekFrom::Start(self.data_offset))?;
+
+ let full_image_size =
+ num_bytes(self.width, self.height, self.num_channels()).ok_or_else(|| {
+ ImageError::FormatError("Image buffer would be too large!".to_owned())
+ })?;
+ let mut pixel_data = self.create_pixel_data();
+ let (skip_pixels, skip_rows, eof_hit) =
+ self.read_rle_data_step(&mut pixel_data, image_type, 0, 0)?;
+ // Extend the buffer if there is still data left.
+ // If eof_hit is true, it means that we hit an end-of-file marker in the last step and
+ // we won't extend the buffer further to avoid small files with a large specified size causing memory issues.
+ // This is only a rudimentary check, a file could still create a large buffer, but the
+ // file would now have to at least have some data in it.
+ if pixel_data.len() < full_image_size && !eof_hit {
+ let new = extend_buffer(&mut pixel_data, full_image_size, true);
+ self.read_rle_data_step(new, image_type, skip_pixels, skip_rows)?;
+ }
+ Ok(pixel_data)
+ }
+
+ fn read_rle_data_step(
+ &mut self,
+ mut pixel_data: &mut [u8],
+ image_type: ImageType,
+ skip_pixels: u8,
+ skip_rows: u8,
+ ) -> ImageResult<(u8, u8, bool)> {
+ let num_channels = self.num_channels();
+
+ let mut delta_rows_left = 0;
+ let mut delta_pixels_left = skip_pixels;
+ let mut eof_hit = false;
+
+ // Scope the borrowing of pixel_data by the row iterator.
+ {
+ // Handling deltas in the RLE scheme means that we need to manually
+ // iterate through rows and pixels. Even if we didn't have to handle
+ // deltas, we have to ensure that a single runlength doesn't straddle
+ // two rows.
+ let mut row_iter = self.rows(&mut pixel_data);
+ // If we have previously hit a delta value,
+ // blank the rows that are to be skipped.
+ blank_bytes((&mut row_iter).take(skip_rows.into()));
+ let mut insns_iter = RLEInsnIterator {
+ r: &mut self.reader,
+ image_type,
+ };
+ let p = self.palette.as_ref().unwrap();
+
+ 'row_loop: while let Some(row) = row_iter.next() {
+ let mut pixel_iter = row.chunks_mut(num_channels);
+ // Blank delta skipped pixels if any.
+ blank_bytes((&mut pixel_iter).take(delta_pixels_left.into()));
+ delta_pixels_left = 0;
+
+ 'rle_loop: loop {
+ if let Some(insn) = insns_iter.next() {
+ match insn {
+ RLEInsn::EndOfFile => {
+ blank_bytes(pixel_iter);
+ blank_bytes(row_iter);
+ eof_hit = true;
+ break 'row_loop;
+ }
+ RLEInsn::EndOfRow => {
+ blank_bytes(pixel_iter);
+ break 'rle_loop;
+ }
+ RLEInsn::Delta(x_delta, y_delta) => {
+ if y_delta > 0 {
+ for n in 1..y_delta {
+ if let Some(row) = row_iter.next() {
+ // The msdn site on bitmap compression doesn't specify
+ // what happens to the values skipped when encountering
+ // a delta code, however IE and the windows image
+ // preview seems to replace them with black pixels,
+ // so we stick to that.
+ for b in row {
+ *b = 0;
+ }
+ } else {
+ delta_pixels_left = x_delta;
+ // We've reached the end of the buffer.
+ delta_rows_left = y_delta - n;
+ break 'row_loop;
+ }
+ }
+ }
+
+ for _ in 0..x_delta {
+ if let Some(pixel) = pixel_iter.next() {
+ for b in pixel {
+ *b = 0;
+ }
+ } else {
+ // We can't go any further in this row.
+ break;
+ }
+ }
+ }
+ RLEInsn::Absolute(length, indices) => {
+ // Absolute mode cannot span rows, so if we run
+ // out of pixels to process, we should stop
+ // processing the image.
+ match image_type {
+ ImageType::RLE8 => {
+ if !set_8bit_pixel_run(
+ &mut pixel_iter,
+ p,
+ indices.iter(),
+ length as usize,
+ ) {
+ break 'row_loop;
+ }
+ }
+ ImageType::RLE4 => {
+ if !set_4bit_pixel_run(
+ &mut pixel_iter,
+ p,
+ indices.iter(),
+ length as usize,
+ ) {
+ break 'row_loop;
+ }
+ }
+ _ => panic!(),
+ }
+ }
+ RLEInsn::PixelRun(n_pixels, palette_index) => {
+ // A pixel run isn't allowed to span rows, but we
+ // simply continue on to the next row if we run
+ // out of pixels to set.
+ match image_type {
+ ImageType::RLE8 => {
+ if !set_8bit_pixel_run(
+ &mut pixel_iter,
+ p,
+ repeat(&palette_index),
+ n_pixels as usize,
+ ) {
+ break 'rle_loop;
+ }
+ }
+ ImageType::RLE4 => {
+ if !set_4bit_pixel_run(
+ &mut pixel_iter,
+ p,
+ repeat(&palette_index),
+ n_pixels as usize,
+ ) {
+ break 'rle_loop;
+ }
+ }
+ _ => panic!(),
+ }
+ }
+ }
+ } else {
+ // We ran out of data while we still had rows to fill in.
+ return Err(ImageError::FormatError("Not enough RLE data".to_string()));
+ }
+ }
+ }
+ }
+ Ok((delta_pixels_left, delta_rows_left, eof_hit))
+ }
+
+ /// Read the actual data of the image. This function is deliberately not public because it
+ /// cannot be called multiple times without seeking back the underlying reader in between.
+ pub(crate) fn read_image_data(&mut self, buf: &mut [u8]) -> ImageResult<()> {
+ let data = match self.image_type {
+ ImageType::Palette => self.read_palettized_pixel_data(),
+ ImageType::RGB16 => self.read_16_bit_pixel_data(Some(&R5_G5_B5_COLOR_MASK)),
+ ImageType::RGB24 => self.read_full_byte_pixel_data(&FormatFullBytes::RGB24),
+ ImageType::RGB32 => self.read_full_byte_pixel_data(&FormatFullBytes::RGB32),
+ ImageType::RGBA32 => self.read_full_byte_pixel_data(&FormatFullBytes::RGBA32),
+ ImageType::RLE8 => self.read_rle_data(ImageType::RLE8),
+ ImageType::RLE4 => self.read_rle_data(ImageType::RLE4),
+ ImageType::Bitfields16 => match self.bitfields {
+ Some(_) => self.read_16_bit_pixel_data(None),
+ None => Err(ImageError::FormatError(
+ "Missing 16-bit bitfield masks".to_string(),
+ )),
+ },
+ ImageType::Bitfields32 => match self.bitfields {
+ Some(R8_G8_B8_COLOR_MASK) => {
+ self.read_full_byte_pixel_data(&FormatFullBytes::Format888)
+ }
+ Some(_) => self.read_32_bit_pixel_data(),
+ None => Err(ImageError::FormatError(
+ "Missing 32-bit bitfield masks".to_string(),
+ )),
+ },
+ }?;
+
+ buf.copy_from_slice(&data);
+ Ok(())
+ }
+}
+
+/// Wrapper struct around a `Cursor<Vec<u8>>`
+pub struct BmpReader<R>(Cursor<Vec<u8>>, PhantomData<R>);
+impl<R> Read for BmpReader<R> {
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ self.0.read(buf)
+ }
+ fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
+ if self.0.position() == 0 && buf.is_empty() {
+ mem::swap(buf, self.0.get_mut());
+ Ok(buf.len())
+ } else {
+ self.0.read_to_end(buf)
+ }
+ }
+}
+
+impl<'a, R: 'a + Read + Seek> ImageDecoder<'a> for BmpDecoder<R> {
+ type Reader = BmpReader<R>;
+
+ fn dimensions(&self) -> (u32, u32) {
+ (self.width as u32, self.height as u32)
+ }
+
+ fn color_type(&self) -> ColorType {
+ if self.add_alpha_channel {
+ ColorType::Rgba8
+ } else {
+ ColorType::Rgb8
+ }
+ }
+
+ fn into_reader(self) -> ImageResult<Self::Reader> {
+ Ok(BmpReader(Cursor::new(image::decoder_to_vec(self)?), PhantomData))
+ }
+
+ fn read_image(mut self, buf: &mut [u8]) -> ImageResult<()> {
+ assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes()));
+ self.read_image_data(buf)
+ }
+}
+
+impl<'a, R: 'a + Read + Seek> ImageDecoderExt<'a> for BmpDecoder<R> {
+ fn read_rect_with_progress<F: Fn(Progress)>(
+ &mut self,
+ x: u32,
+ y: u32,
+ width: u32,
+ height: u32,
+ buf: &mut [u8],
+ progress_callback: F,
+ ) -> ImageResult<()> {
+ let start = self.reader.seek(SeekFrom::Current(0))?;
+ image::load_rect(x, y, width, height, buf, progress_callback, self, |_, _| unreachable!(),
+ |s, buf| { s.read_image_data(buf).map(|_| buf.len()) })?;
+ self.reader.seek(SeekFrom::Start(start))?;
+ Ok(())
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::Bitfield;
+
+ #[test]
+ fn test_bitfield_len() {
+ for len in 1..9 {
+ let bitfield = Bitfield { shift: 0, len };
+ for i in 0..(1 << len) {
+ let read = bitfield.read(i);
+ let calc = (i as f64 / ((1 << len) - 1) as f64 * 255f64).round() as u8;
+ if read != calc {
+ println!("len:{} i:{} read:{} calc:{}", len, i, read, calc);
+ }
+ assert_eq!(read, calc);
+ }
+ }
+ }
+}
diff --git a/third_party/rust/image/src/bmp/encoder.rs b/third_party/rust/image/src/bmp/encoder.rs
new file mode 100644
index 0000000000..cceffef320
--- /dev/null
+++ b/third_party/rust/image/src/bmp/encoder.rs
@@ -0,0 +1,348 @@
+use byteorder::{LittleEndian, WriteBytesExt};
+use std::io::{self, Write};
+
+use crate::color;
+use crate::error::{ImageError, ImageResult};
+use crate::image::ImageEncoder;
+
+const BITMAPFILEHEADER_SIZE: u32 = 14;
+const BITMAPINFOHEADER_SIZE: u32 = 40;
+const BITMAPV4HEADER_SIZE: u32 = 108;
+
+/// The representation of a BMP encoder.
+pub struct BMPEncoder<'a, W: 'a> {
+ writer: &'a mut W,
+}
+
+impl<'a, W: Write + 'a> BMPEncoder<'a, W> {
+ /// Create a new encoder that writes its output to ```w```.
+ pub fn new(w: &'a mut W) -> Self {
+ BMPEncoder { writer: w }
+ }
+
+ /// Encodes the image ```image```
+ /// that has dimensions ```width``` and ```height```
+ /// and ```ColorType``` ```c```.
+ pub fn encode(
+ &mut self,
+ image: &[u8],
+ width: u32,
+ height: u32,
+ c: color::ColorType,
+ ) -> ImageResult<()> {
+ let bmp_header_size = BITMAPFILEHEADER_SIZE;
+
+ let (dib_header_size, written_pixel_size, palette_color_count) = get_pixel_info(c)?;
+ let row_pad_size = (4 - (width * written_pixel_size) % 4) % 4; // each row must be padded to a multiple of 4 bytes
+ let image_size = width
+ .checked_mul(height)
+ .ok_or(ImageError::DimensionError)?
+ .checked_mul(written_pixel_size)
+ .ok_or(ImageError::DimensionError)?
+ .checked_add(height * row_pad_size)
+ .ok_or(ImageError::DimensionError)?;
+ let palette_size = palette_color_count * 4; // all palette colors are BGRA
+ let file_size = bmp_header_size + dib_header_size + palette_size + image_size;
+
+ // write BMP header
+ self.writer.write_u8(b'B')?;
+ self.writer.write_u8(b'M')?;
+ self.writer.write_u32::<LittleEndian>(file_size)?; // file size
+ self.writer.write_u16::<LittleEndian>(0)?; // reserved 1
+ self.writer.write_u16::<LittleEndian>(0)?; // reserved 2
+ self.writer
+ .write_u32::<LittleEndian>(bmp_header_size + dib_header_size + palette_size)?; // image data offset
+
+ // write DIB header
+ self.writer.write_u32::<LittleEndian>(dib_header_size)?;
+ self.writer.write_i32::<LittleEndian>(width as i32)?;
+ self.writer.write_i32::<LittleEndian>(height as i32)?;
+ self.writer.write_u16::<LittleEndian>(1)?; // color planes
+ self.writer
+ .write_u16::<LittleEndian>((written_pixel_size * 8) as u16)?; // bits per pixel
+ if dib_header_size >= BITMAPV4HEADER_SIZE {
+ // Assume BGRA32
+ self.writer.write_u32::<LittleEndian>(3)?; // compression method - bitfields
+ } else {
+ self.writer.write_u32::<LittleEndian>(0)?; // compression method - no compression
+ }
+ self.writer.write_u32::<LittleEndian>(image_size)?;
+ self.writer.write_i32::<LittleEndian>(0)?; // horizontal ppm
+ self.writer.write_i32::<LittleEndian>(0)?; // vertical ppm
+ self.writer.write_u32::<LittleEndian>(palette_color_count)?;
+ self.writer.write_u32::<LittleEndian>(0)?; // all colors are important
+ if dib_header_size >= BITMAPV4HEADER_SIZE {
+ // Assume BGRA32
+ self.writer.write_u32::<LittleEndian>(0xff << 16)?; // red mask
+ self.writer.write_u32::<LittleEndian>(0xff << 8)?; // green mask
+ self.writer.write_u32::<LittleEndian>(0xff << 0)?; // blue mask
+ self.writer.write_u32::<LittleEndian>(0xff << 24)?; // alpha mask
+ self.writer.write_u32::<LittleEndian>(0x73524742)?; // colorspace - sRGB
+
+ // endpoints (3x3) and gamma (3)
+ for _ in 0..12 {
+ self.writer.write_u32::<LittleEndian>(0)?;
+ }
+ }
+
+ // write image data
+ match c {
+ color::ColorType::Rgb8 => {
+ self.encode_rgb(image, width, height, row_pad_size, 3)?
+ }
+ color::ColorType::Rgba8 => {
+ self.encode_rgba(image, width, height, row_pad_size, 4)?
+ }
+ color::ColorType::L8 => {
+ self.encode_gray(image, width, height, row_pad_size, 1)?
+ }
+ color::ColorType::La8 => {
+ self.encode_gray(image, width, height, row_pad_size, 2)?
+ }
+ _ => {
+ return Err(ImageError::IoError(io::Error::new(
+ io::ErrorKind::InvalidInput,
+ &get_unsupported_error_message(c)[..],
+ )))
+ }
+ }
+
+ Ok(())
+ }
+
+ fn encode_rgb(
+ &mut self,
+ image: &[u8],
+ width: u32,
+ height: u32,
+ row_pad_size: u32,
+ bytes_per_pixel: u32,
+ ) -> io::Result<()> {
+ let x_stride = bytes_per_pixel;
+ let y_stride = width * x_stride;
+ for row in 0..height {
+ // from the bottom up
+ let row_start = (height - row - 1) * y_stride;
+ for col in 0..width {
+ let pixel_start = (row_start + (col * x_stride)) as usize;
+ let r = image[pixel_start];
+ let g = image[pixel_start + 1];
+ let b = image[pixel_start + 2];
+ // written as BGR
+ self.writer.write_u8(b)?;
+ self.writer.write_u8(g)?;
+ self.writer.write_u8(r)?;
+ // alpha is never written as it's not widely supported
+ }
+
+ self.write_row_pad(row_pad_size)?;
+ }
+
+ Ok(())
+ }
+
+ fn encode_rgba(
+ &mut self,
+ image: &[u8],
+ width: u32,
+ height: u32,
+ row_pad_size: u32,
+ bytes_per_pixel: u32,
+ ) -> io::Result<()> {
+ let x_stride = bytes_per_pixel;
+ let y_stride = width * x_stride;
+ for row in 0..height {
+ // from the bottom up
+ let row_start = (height - row - 1) * y_stride;
+ for col in 0..width {
+ let pixel_start = (row_start + (col * x_stride)) as usize;
+ let r = image[pixel_start];
+ let g = image[pixel_start + 1];
+ let b = image[pixel_start + 2];
+ let a = image[pixel_start + 3];
+ // written as BGRA
+ self.writer.write_u8(b)?;
+ self.writer.write_u8(g)?;
+ self.writer.write_u8(r)?;
+ self.writer.write_u8(a)?;
+ }
+
+ self.write_row_pad(row_pad_size)?;
+ }
+
+ Ok(())
+ }
+
+ fn encode_gray(
+ &mut self,
+ image: &[u8],
+ width: u32,
+ height: u32,
+ row_pad_size: u32,
+ bytes_per_pixel: u32,
+ ) -> io::Result<()> {
+ // write grayscale palette
+ for val in 0..256 {
+ // each color is written as BGRA, where A is always 0 and since only grayscale is being written, B = G = R = index
+ let val = val as u8;
+ self.writer.write_u8(val)?;
+ self.writer.write_u8(val)?;
+ self.writer.write_u8(val)?;
+ self.writer.write_u8(0)?;
+ }
+
+ // write image data
+ let x_stride = bytes_per_pixel;
+ let y_stride = width * x_stride;
+ for row in 0..height {
+ // from the bottom up
+ let row_start = (height - row - 1) * y_stride;
+ for col in 0..width {
+ let pixel_start = (row_start + (col * x_stride)) as usize;
+ // color value is equal to the palette index
+ self.writer.write_u8(image[pixel_start])?;
+ // alpha is never written as it's not widely supported
+ }
+
+ self.write_row_pad(row_pad_size)?;
+ }
+
+ Ok(())
+ }
+
+ fn write_row_pad(&mut self, row_pad_size: u32) -> io::Result<()> {
+ for _ in 0..row_pad_size {
+ self.writer.write_u8(0)?;
+ }
+
+ Ok(())
+ }
+}
+
+impl<'a, W: Write> ImageEncoder for BMPEncoder<'a, W> {
+ fn write_image(
+ mut self,
+ buf: &[u8],
+ width: u32,
+ height: u32,
+ color_type: color::ColorType,
+ ) -> ImageResult<()> {
+ self.encode(buf, width, height, color_type)
+ }
+}
+
+fn get_unsupported_error_message(c: color::ColorType) -> String {
+ format!(
+ "Unsupported color type {:?}. Supported types: RGB(8), RGBA(8), Gray(8), GrayA(8).",
+ c
+ )
+}
+
+/// Returns a tuple representing: (dib header size, written pixel size, palette color count).
+fn get_pixel_info(c: color::ColorType) -> io::Result<(u32, u32, u32)> {
+ let sizes = match c {
+ color::ColorType::Rgb8 => (BITMAPINFOHEADER_SIZE, 3, 0),
+ color::ColorType::Rgba8 => (BITMAPV4HEADER_SIZE, 4, 0),
+ color::ColorType::L8 => (BITMAPINFOHEADER_SIZE, 1, 256),
+ color::ColorType::La8 => (BITMAPINFOHEADER_SIZE, 1, 256),
+ _ => {
+ return Err(io::Error::new(
+ io::ErrorKind::InvalidInput,
+ &get_unsupported_error_message(c)[..],
+ ))
+ }
+ };
+
+ Ok(sizes)
+}
+
+#[cfg(test)]
+mod tests {
+ use super::super::BmpDecoder;
+ use super::BMPEncoder;
+ use crate::color::ColorType;
+ use crate::image::ImageDecoder;
+ use std::io::Cursor;
+
+ fn round_trip_image(image: &[u8], width: u32, height: u32, c: ColorType) -> Vec<u8> {
+ let mut encoded_data = Vec::new();
+ {
+ let mut encoder = BMPEncoder::new(&mut encoded_data);
+ encoder
+ .encode(&image, width, height, c)
+ .expect("could not encode image");
+ }
+
+ let decoder = BmpDecoder::new(Cursor::new(&encoded_data)).expect("failed to decode");
+
+ let mut buf = vec![0; decoder.total_bytes() as usize];
+ decoder.read_image(&mut buf).expect("failed to decode");
+ buf
+ }
+
+ #[test]
+ fn round_trip_single_pixel_rgb() {
+ let image = [255u8, 0, 0]; // single red pixel
+ let decoded = round_trip_image(&image, 1, 1, ColorType::Rgb8);
+ assert_eq!(3, decoded.len());
+ assert_eq!(255, decoded[0]);
+ assert_eq!(0, decoded[1]);
+ assert_eq!(0, decoded[2]);
+ }
+
+ #[test]
+ fn huge_files_return_error() {
+ let mut encoded_data = Vec::new();
+ let image = vec![0u8; 3 * 40_000 * 40_000]; // 40_000x40_000 pixels, 3 bytes per pixel, allocated on the heap
+ let mut encoder = BMPEncoder::new(&mut encoded_data);
+ let result = encoder.encode(&image, 40_000, 40_000, ColorType::Rgb8);
+ assert!(result.is_err());
+ }
+
+ #[test]
+ fn round_trip_single_pixel_rgba() {
+ let image = [1, 2, 3, 4];
+ let decoded = round_trip_image(&image, 1, 1, ColorType::Rgba8);
+ assert_eq!(&decoded[..], &image[..]);
+ }
+
+ #[test]
+ fn round_trip_3px_rgb() {
+ let image = [0u8; 3 * 3 * 3]; // 3x3 pixels, 3 bytes per pixel
+ let _decoded = round_trip_image(&image, 3, 3, ColorType::Rgb8);
+ }
+
+ #[test]
+ fn round_trip_gray() {
+ let image = [0u8, 1, 2]; // 3 pixels
+ let decoded = round_trip_image(&image, 3, 1, ColorType::L8);
+ // should be read back as 3 RGB pixels
+ assert_eq!(9, decoded.len());
+ assert_eq!(0, decoded[0]);
+ assert_eq!(0, decoded[1]);
+ assert_eq!(0, decoded[2]);
+ assert_eq!(1, decoded[3]);
+ assert_eq!(1, decoded[4]);
+ assert_eq!(1, decoded[5]);
+ assert_eq!(2, decoded[6]);
+ assert_eq!(2, decoded[7]);
+ assert_eq!(2, decoded[8]);
+ }
+
+ #[test]
+ fn round_trip_graya() {
+ let image = [0u8, 0, 1, 0, 2, 0]; // 3 pixels, each with an alpha channel
+ let decoded = round_trip_image(&image, 1, 3, ColorType::La8);
+ // should be read back as 3 RGB pixels
+ assert_eq!(9, decoded.len());
+ assert_eq!(0, decoded[0]);
+ assert_eq!(0, decoded[1]);
+ assert_eq!(0, decoded[2]);
+ assert_eq!(1, decoded[3]);
+ assert_eq!(1, decoded[4]);
+ assert_eq!(1, decoded[5]);
+ assert_eq!(2, decoded[6]);
+ assert_eq!(2, decoded[7]);
+ assert_eq!(2, decoded[8]);
+ }
+}
diff --git a/third_party/rust/image/src/bmp/mod.rs b/third_party/rust/image/src/bmp/mod.rs
new file mode 100644
index 0000000000..09f80ba3d2
--- /dev/null
+++ b/third_party/rust/image/src/bmp/mod.rs
@@ -0,0 +1,14 @@
+//! Decoding and Encoding of BMP Images
+//!
+//! A decoder and encoder for BMP (Windows Bitmap) images
+//!
+//! # Related Links
+//! * <https://msdn.microsoft.com/en-us/library/windows/desktop/dd183375%28v=vs.85%29.aspx>
+//! * <https://en.wikipedia.org/wiki/BMP_file_format>
+//!
+
+pub use self::decoder::BmpDecoder;
+pub use self::encoder::BMPEncoder;
+
+mod decoder;
+mod encoder;