1484 lines
51 KiB
Rust
1484 lines
51 KiB
Rust
|
use std::cmp::{self, Ordering};
|
|||
|
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::{error, fmt, mem};
|
|||
|
|
|||
|
use byteorder::{LittleEndian, ReadBytesExt};
|
|||
|
|
|||
|
use crate::color::ColorType;
|
|||
|
use crate::error::{
|
|||
|
DecodingError, ImageError, ImageResult, UnsupportedError, UnsupportedErrorKind,
|
|||
|
};
|
|||
|
use crate::image::{self, ImageDecoder, ImageDecoderRect, ImageFormat, 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 R8_G8_B8_A8_COLOR_MASK: Bitfields = Bitfields {
|
|||
|
r: Bitfield { len: 8, shift: 16 },
|
|||
|
g: Bitfield { len: 8, shift: 8 },
|
|||
|
b: Bitfield { len: 8, shift: 0 },
|
|||
|
a: Bitfield { len: 8, shift: 24 },
|
|||
|
};
|
|||
|
|
|||
|
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(),
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// All errors that can occur when attempting to parse a BMP
|
|||
|
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
|||
|
enum DecoderError {
|
|||
|
// Failed to decompress RLE data.
|
|||
|
CorruptRleData,
|
|||
|
|
|||
|
/// The bitfield mask interleaves set and unset bits
|
|||
|
BitfieldMaskNonContiguous,
|
|||
|
/// Bitfield mask invalid (e.g. too long for specified type)
|
|||
|
BitfieldMaskInvalid,
|
|||
|
/// Bitfield (of the specified width – 16- or 32-bit) mask not present
|
|||
|
BitfieldMaskMissing(u32),
|
|||
|
/// Bitfield (of the specified width – 16- or 32-bit) masks not present
|
|||
|
BitfieldMasksMissing(u32),
|
|||
|
|
|||
|
/// BMP's "BM" signature wrong or missing
|
|||
|
BmpSignatureInvalid,
|
|||
|
/// More than the exactly one allowed plane specified by the format
|
|||
|
MoreThanOnePlane,
|
|||
|
/// Invalid amount of bits per channel for the specified image type
|
|||
|
InvalidChannelWidth(ChannelWidthError, u16),
|
|||
|
|
|||
|
/// The width is negative
|
|||
|
NegativeWidth(i32),
|
|||
|
/// One of the dimensions is larger than a soft limit
|
|||
|
ImageTooLarge(i32, i32),
|
|||
|
/// The height is `i32::min_value()`
|
|||
|
///
|
|||
|
/// General negative heights specify top-down DIBs
|
|||
|
InvalidHeight,
|
|||
|
|
|||
|
/// Specified image type is invalid for top-down BMPs (i.e. is compressed)
|
|||
|
ImageTypeInvalidForTopDown(u32),
|
|||
|
/// Image type not currently recognized by the decoder
|
|||
|
ImageTypeUnknown(u32),
|
|||
|
|
|||
|
/// Bitmap header smaller than the core header
|
|||
|
HeaderTooSmall(u32),
|
|||
|
|
|||
|
/// The palette is bigger than allowed by the bit count of the BMP
|
|||
|
PaletteSizeExceeded {
|
|||
|
colors_used: u32,
|
|||
|
bit_count: u16,
|
|||
|
},
|
|||
|
}
|
|||
|
|
|||
|
impl fmt::Display for DecoderError {
|
|||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|||
|
match self {
|
|||
|
DecoderError::CorruptRleData => f.write_str("Corrupt RLE data"),
|
|||
|
DecoderError::BitfieldMaskNonContiguous => f.write_str("Non-contiguous bitfield mask"),
|
|||
|
DecoderError::BitfieldMaskInvalid => f.write_str("Invalid bitfield mask"),
|
|||
|
DecoderError::BitfieldMaskMissing(bb) => {
|
|||
|
f.write_fmt(format_args!("Missing {}-bit bitfield mask", bb))
|
|||
|
}
|
|||
|
DecoderError::BitfieldMasksMissing(bb) => {
|
|||
|
f.write_fmt(format_args!("Missing {}-bit bitfield masks", bb))
|
|||
|
}
|
|||
|
DecoderError::BmpSignatureInvalid => f.write_str("BMP signature not found"),
|
|||
|
DecoderError::MoreThanOnePlane => f.write_str("More than one plane"),
|
|||
|
DecoderError::InvalidChannelWidth(tp, n) => {
|
|||
|
f.write_fmt(format_args!("Invalid channel bit count for {}: {}", tp, n))
|
|||
|
}
|
|||
|
DecoderError::NegativeWidth(w) => f.write_fmt(format_args!("Negative width ({})", w)),
|
|||
|
DecoderError::ImageTooLarge(w, h) => f.write_fmt(format_args!(
|
|||
|
"Image too large (one of ({}, {}) > soft limit of {})",
|
|||
|
w, h, MAX_WIDTH_HEIGHT
|
|||
|
)),
|
|||
|
DecoderError::InvalidHeight => f.write_str("Invalid height"),
|
|||
|
DecoderError::ImageTypeInvalidForTopDown(tp) => f.write_fmt(format_args!(
|
|||
|
"Invalid image type {} for top-down image.",
|
|||
|
tp
|
|||
|
)),
|
|||
|
DecoderError::ImageTypeUnknown(tp) => {
|
|||
|
f.write_fmt(format_args!("Unknown image compression type {}", tp))
|
|||
|
}
|
|||
|
DecoderError::HeaderTooSmall(s) => {
|
|||
|
f.write_fmt(format_args!("Bitmap header too small ({} bytes)", s))
|
|||
|
}
|
|||
|
DecoderError::PaletteSizeExceeded {
|
|||
|
colors_used,
|
|||
|
bit_count,
|
|||
|
} => f.write_fmt(format_args!(
|
|||
|
"Palette size {} exceeds maximum size for BMP with bit count of {}",
|
|||
|
colors_used, bit_count
|
|||
|
)),
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
impl From<DecoderError> for ImageError {
|
|||
|
fn from(e: DecoderError) -> ImageError {
|
|||
|
ImageError::Decoding(DecodingError::new(ImageFormat::Bmp.into(), e))
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
impl error::Error for DecoderError {}
|
|||
|
|
|||
|
/// Distinct image types whose saved channel width can be invalid
|
|||
|
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
|||
|
enum ChannelWidthError {
|
|||
|
/// RGB
|
|||
|
Rgb,
|
|||
|
/// 8-bit run length encoding
|
|||
|
Rle8,
|
|||
|
/// 4-bit run length encoding
|
|||
|
Rle4,
|
|||
|
/// Bitfields (16- or 32-bit)
|
|||
|
Bitfields,
|
|||
|
}
|
|||
|
|
|||
|
impl fmt::Display for ChannelWidthError {
|
|||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|||
|
f.write_str(match self {
|
|||
|
ChannelWidthError::Rgb => "RGB",
|
|||
|
ChannelWidthError::Rle8 => "RLE8",
|
|||
|
ChannelWidthError::Rle4 => "RLE4",
|
|||
|
ChannelWidthError::Bitfields => "bitfields",
|
|||
|
})
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// 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::Unsupported(UnsupportedError::from_format_and_kind(
|
|||
|
ImageFormat::Bmp.into(),
|
|||
|
UnsupportedErrorKind::GenericFeature(format!(
|
|||
|
"Image dimensions ({}x{} w/{} channels) are too large",
|
|||
|
width, length, channels
|
|||
|
)),
|
|||
|
))
|
|||
|
})
|
|||
|
}
|
|||
|
|
|||
|
/// 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,
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// 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 [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();
|
|||
|
assert_eq!(buffer.len(), full_image_size);
|
|||
|
|
|||
|
if !top_down {
|
|||
|
for row in buffer.chunks_mut(row_width).rev() {
|
|||
|
func(row)?;
|
|||
|
}
|
|||
|
} else {
|
|||
|
for row in buffer.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; 3]],
|
|||
|
indices: T,
|
|||
|
n_pixels: usize,
|
|||
|
) -> bool {
|
|||
|
for idx in indices.take(n_pixels) {
|
|||
|
if let Some(pixel) = pixel_iter.next() {
|
|||
|
let rgb = palette[*idx as usize];
|
|||
|
pixel[0] = rgb[0];
|
|||
|
pixel[1] = rgb[1];
|
|||
|
pixel[2] = rgb[2];
|
|||
|
} else {
|
|||
|
return false;
|
|||
|
}
|
|||
|
}
|
|||
|
true
|
|||
|
}
|
|||
|
|
|||
|
fn set_4bit_pixel_run<'a, T: Iterator<Item = &'a u8>>(
|
|||
|
pixel_iter: &mut ChunksMut<u8>,
|
|||
|
palette: &[[u8; 3]],
|
|||
|
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 rgb = palette[$i as usize];
|
|||
|
pixel[0] = rgb[0];
|
|||
|
pixel[1] = rgb[1];
|
|||
|
pixel[2] = rgb[2];
|
|||
|
} 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; 3]],
|
|||
|
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 rgb = palette[$i as usize];
|
|||
|
pixel[0] = rgb[0];
|
|||
|
pixel[1] = rgb[1];
|
|||
|
pixel[2] = rgb[2];
|
|||
|
} 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; 3]],
|
|||
|
indices: T,
|
|||
|
) {
|
|||
|
for idx in indices {
|
|||
|
let mut bit = 0x80;
|
|||
|
loop {
|
|||
|
if let Some(pixel) = pixel_iter.next() {
|
|||
|
let rgb = palette[((idx & bit) != 0) as usize];
|
|||
|
pixel[0] = rgb[0];
|
|||
|
pixel[1] = rgb[1];
|
|||
|
pixel[2] = rgb[2];
|
|||
|
} 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(DecoderError::BitfieldMaskNonContiguous.into());
|
|||
|
}
|
|||
|
if len + shift > max_len {
|
|||
|
return Err(DecoderError::BitfieldMaskInvalid.into());
|
|||
|
}
|
|||
|
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(DecoderError::BitfieldMaskMissing(max_len).into());
|
|||
|
}
|
|||
|
Ok(bitfields)
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// A bmp decoder
|
|||
|
pub struct BmpDecoder<R> {
|
|||
|
reader: R,
|
|||
|
|
|||
|
bmp_header_type: BMPHeaderType,
|
|||
|
indexed_color: bool,
|
|||
|
|
|||
|
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; 3]>>,
|
|||
|
bitfields: Option<Bitfields>,
|
|||
|
}
|
|||
|
|
|||
|
enum RLEInsn {
|
|||
|
EndOfFile,
|
|||
|
EndOfRow,
|
|||
|
Delta(u8, u8),
|
|||
|
Absolute(u8, Vec<u8>),
|
|||
|
PixelRun(u8, u8),
|
|||
|
}
|
|||
|
|
|||
|
impl<R: Read + Seek> BmpDecoder<R> {
|
|||
|
fn new_decoder(reader: R) -> BmpDecoder<R> {
|
|||
|
BmpDecoder {
|
|||
|
reader,
|
|||
|
|
|||
|
bmp_header_type: BMPHeaderType::Info,
|
|||
|
indexed_color: false,
|
|||
|
|
|||
|
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,
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// Create a new decoder that decodes from the stream ```r```
|
|||
|
pub fn new(reader: R) -> ImageResult<BmpDecoder<R>> {
|
|||
|
let mut decoder = Self::new_decoder(reader);
|
|||
|
decoder.read_metadata()?;
|
|||
|
Ok(decoder)
|
|||
|
}
|
|||
|
|
|||
|
/// Create a new decoder that decodes from the stream ```r``` without first
|
|||
|
/// reading a BITMAPFILEHEADER. This is useful for decoding the CF_DIB format
|
|||
|
/// directly from the Windows clipboard.
|
|||
|
pub fn new_without_file_header(reader: R) -> ImageResult<BmpDecoder<R>> {
|
|||
|
let mut decoder = Self::new_decoder(reader);
|
|||
|
decoder.no_file_header = true;
|
|||
|
decoder.read_metadata()?;
|
|||
|
Ok(decoder)
|
|||
|
}
|
|||
|
|
|||
|
#[cfg(feature = "ico")]
|
|||
|
pub(crate) fn new_with_ico_format(reader: R) -> ImageResult<BmpDecoder<R>> {
|
|||
|
let mut decoder = Self::new_decoder(reader);
|
|||
|
decoder.read_metadata_in_ico_format()?;
|
|||
|
Ok(decoder)
|
|||
|
}
|
|||
|
|
|||
|
/// If true, the palette in BMP does not apply to the image even if it is found.
|
|||
|
/// In other words, the output image is the indexed color.
|
|||
|
pub fn set_indexed_color(&mut self, indexed_color: bool) {
|
|||
|
self.indexed_color = indexed_color;
|
|||
|
}
|
|||
|
|
|||
|
#[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(DecoderError::BmpSignatureInvalid.into());
|
|||
|
}
|
|||
|
|
|||
|
// 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(DecoderError::MoreThanOnePlane.into());
|
|||
|
}
|
|||
|
|
|||
|
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(DecoderError::InvalidChannelWidth(
|
|||
|
ChannelWidthError::Rgb,
|
|||
|
self.bit_count,
|
|||
|
)
|
|||
|
.into())
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
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(DecoderError::NegativeWidth(self.width).into());
|
|||
|
} 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(DecoderError::ImageTooLarge(self.width, self.height).into());
|
|||
|
}
|
|||
|
|
|||
|
if self.height == i32::min_value() {
|
|||
|
return Err(DecoderError::InvalidHeight.into());
|
|||
|
}
|
|||
|
|
|||
|
// 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(DecoderError::MoreThanOnePlane.into());
|
|||
|
}
|
|||
|
|
|||
|
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(DecoderError::ImageTypeInvalidForTopDown(image_type_u32).into());
|
|||
|
}
|
|||
|
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(DecoderError::InvalidChannelWidth(
|
|||
|
ChannelWidthError::Rgb,
|
|||
|
self.bit_count,
|
|||
|
)
|
|||
|
.into())
|
|||
|
}
|
|||
|
},
|
|||
|
1 => match self.bit_count {
|
|||
|
8 => ImageType::RLE8,
|
|||
|
_ => {
|
|||
|
return Err(DecoderError::InvalidChannelWidth(
|
|||
|
ChannelWidthError::Rle8,
|
|||
|
self.bit_count,
|
|||
|
)
|
|||
|
.into())
|
|||
|
}
|
|||
|
},
|
|||
|
2 => match self.bit_count {
|
|||
|
4 => ImageType::RLE4,
|
|||
|
_ => {
|
|||
|
return Err(DecoderError::InvalidChannelWidth(
|
|||
|
ChannelWidthError::Rle4,
|
|||
|
self.bit_count,
|
|||
|
)
|
|||
|
.into())
|
|||
|
}
|
|||
|
},
|
|||
|
3 => match self.bit_count {
|
|||
|
16 => ImageType::Bitfields16,
|
|||
|
32 => ImageType::Bitfields32,
|
|||
|
_ => {
|
|||
|
return Err(DecoderError::InvalidChannelWidth(
|
|||
|
ChannelWidthError::Bitfields,
|
|||
|
self.bit_count,
|
|||
|
)
|
|||
|
.into())
|
|||
|
}
|
|||
|
},
|
|||
|
4 => {
|
|||
|
// JPEG compression is not implemented yet.
|
|||
|
return Err(ImageError::Unsupported(
|
|||
|
UnsupportedError::from_format_and_kind(
|
|||
|
ImageFormat::Bmp.into(),
|
|||
|
UnsupportedErrorKind::GenericFeature("JPEG compression".to_owned()),
|
|||
|
),
|
|||
|
));
|
|||
|
}
|
|||
|
5 => {
|
|||
|
// PNG compression is not implemented yet.
|
|||
|
return Err(ImageError::Unsupported(
|
|||
|
UnsupportedError::from_format_and_kind(
|
|||
|
ImageFormat::Bmp.into(),
|
|||
|
UnsupportedErrorKind::GenericFeature("PNG compression".to_owned()),
|
|||
|
),
|
|||
|
));
|
|||
|
}
|
|||
|
11 | 12 | 13 => {
|
|||
|
// CMYK types are not implemented yet.
|
|||
|
return Err(ImageError::Unsupported(
|
|||
|
UnsupportedError::from_format_and_kind(
|
|||
|
ImageFormat::Bmp.into(),
|
|||
|
UnsupportedErrorKind::GenericFeature("CMYK format".to_owned()),
|
|||
|
),
|
|||
|
));
|
|||
|
}
|
|||
|
_ => {
|
|||
|
// Unknown compression type.
|
|||
|
return Err(DecoderError::ImageTypeUnknown(image_type_u32).into());
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
// 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.stream_position()?;
|
|||
|
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,
|
|||
|
_ if bmp_header_size < BITMAPCOREHEADER_SIZE => {
|
|||
|
// Size of any valid header types won't be smaller than core header type.
|
|||
|
return Err(DecoderError::HeaderTooSmall(bmp_header_size).into());
|
|||
|
}
|
|||
|
_ => {
|
|||
|
return Err(ImageError::Unsupported(
|
|||
|
UnsupportedError::from_format_and_kind(
|
|||
|
ImageFormat::Bmp.into(),
|
|||
|
UnsupportedErrorKind::GenericFeature(format!(
|
|||
|
"Unknown bitmap header type (size={})",
|
|||
|
bmp_header_size
|
|||
|
)),
|
|||
|
),
|
|||
|
))
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
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.stream_position()?;
|
|||
|
}
|
|||
|
|
|||
|
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(DecoderError::PaletteSizeExceeded {
|
|||
|
colors_used: self.colors_used,
|
|||
|
bit_count: self.bit_count,
|
|||
|
}
|
|||
|
.into());
|
|||
|
}
|
|||
|
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; 3]> = (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(())
|
|||
|
}
|
|||
|
|
|||
|
/// Get the palette that is embedded in the BMP image, if any.
|
|||
|
pub fn get_palette(&self) -> Option<&[[u8; 3]]> {
|
|||
|
self.palette.as_ref().map(|vec| &vec[..])
|
|||
|
}
|
|||
|
|
|||
|
fn num_channels(&self) -> usize {
|
|||
|
if self.indexed_color {
|
|||
|
1
|
|||
|
} else if self.add_alpha_channel {
|
|||
|
4
|
|||
|
} else {
|
|||
|
3
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
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, buf: &mut [u8]) -> ImageResult<()> {
|
|||
|
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;
|
|||
|
let skip_palette = self.indexed_color;
|
|||
|
|
|||
|
reader.seek(SeekFrom::Start(self.data_offset))?;
|
|||
|
|
|||
|
if num_channels == 4 {
|
|||
|
buf.chunks_exact_mut(4).for_each(|c| c[3] = 0xFF);
|
|||
|
}
|
|||
|
|
|||
|
with_rows(
|
|||
|
buf,
|
|||
|
self.width,
|
|||
|
self.height,
|
|||
|
num_channels,
|
|||
|
self.top_down,
|
|||
|
|row| {
|
|||
|
reader.read_exact(&mut indices)?;
|
|||
|
if skip_palette {
|
|||
|
row.clone_from_slice(&indices[0..width]);
|
|||
|
} else {
|
|||
|
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(())
|
|||
|
}
|
|||
|
|
|||
|
fn read_16_bit_pixel_data(
|
|||
|
&mut self,
|
|||
|
buf: &mut [u8],
|
|||
|
bitfields: Option<&Bitfields>,
|
|||
|
) -> ImageResult<()> {
|
|||
|
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(
|
|||
|
buf,
|
|||
|
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 {
|
|||
|
if bitfields.a.len != 0 {
|
|||
|
pixel[3] = bitfields.a.read(data);
|
|||
|
} else {
|
|||
|
pixel[3] = 0xFF;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
reader.read_exact(row_padding)
|
|||
|
},
|
|||
|
)?;
|
|||
|
|
|||
|
Ok(())
|
|||
|
}
|
|||
|
|
|||
|
/// Read image data from a reader in 32-bit formats that use bitfields.
|
|||
|
fn read_32_bit_pixel_data(&mut self, buf: &mut [u8]) -> ImageResult<()> {
|
|||
|
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(
|
|||
|
buf,
|
|||
|
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 {
|
|||
|
if bitfields.a.len != 0 {
|
|||
|
pixel[3] = bitfields.a.read(data);
|
|||
|
} else {
|
|||
|
pixel[3] = 0xff;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
Ok(())
|
|||
|
},
|
|||
|
)?;
|
|||
|
|
|||
|
Ok(())
|
|||
|
}
|
|||
|
|
|||
|
/// 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,
|
|||
|
buf: &mut [u8],
|
|||
|
format: &FormatFullBytes,
|
|||
|
) -> ImageResult<()> {
|
|||
|
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(
|
|||
|
buf,
|
|||
|
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])?;
|
|||
|
} else if num_channels == 4 {
|
|||
|
pixel[3] = 0xFF;
|
|||
|
}
|
|||
|
}
|
|||
|
reader.read_exact(row_padding)
|
|||
|
},
|
|||
|
)?;
|
|||
|
|
|||
|
Ok(())
|
|||
|
}
|
|||
|
|
|||
|
fn read_rle_data(&mut self, buf: &mut [u8], image_type: ImageType) -> ImageResult<()> {
|
|||
|
// Seek to the start of the actual image data.
|
|||
|
self.reader.seek(SeekFrom::Start(self.data_offset))?;
|
|||
|
|
|||
|
let num_channels = self.num_channels();
|
|||
|
let p = self.palette.as_ref().unwrap();
|
|||
|
|
|||
|
// 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(buf);
|
|||
|
|
|||
|
while let Some(row) = row_iter.next() {
|
|||
|
let mut pixel_iter = row.chunks_mut(num_channels);
|
|||
|
|
|||
|
let mut x = 0;
|
|||
|
loop {
|
|||
|
let instruction = {
|
|||
|
let control_byte = self.reader.read_u8()?;
|
|||
|
match control_byte {
|
|||
|
RLE_ESCAPE => {
|
|||
|
let op = self.reader.read_u8()?;
|
|||
|
|
|||
|
match op {
|
|||
|
RLE_ESCAPE_EOL => RLEInsn::EndOfRow,
|
|||
|
RLE_ESCAPE_EOF => RLEInsn::EndOfFile,
|
|||
|
RLE_ESCAPE_DELTA => {
|
|||
|
let xdelta = self.reader.read_u8()?;
|
|||
|
let ydelta = self.reader.read_u8()?;
|
|||
|
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];
|
|||
|
self.reader.read_exact(&mut buffer)?;
|
|||
|
RLEInsn::Absolute(op, buffer)
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
_ => {
|
|||
|
let palette_index = self.reader.read_u8()?;
|
|||
|
RLEInsn::PixelRun(control_byte, palette_index)
|
|||
|
}
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
match instruction {
|
|||
|
RLEInsn::EndOfFile => {
|
|||
|
pixel_iter.for_each(|p| p.fill(0));
|
|||
|
row_iter.for_each(|r| r.fill(0));
|
|||
|
return Ok(());
|
|||
|
}
|
|||
|
RLEInsn::EndOfRow => {
|
|||
|
pixel_iter.for_each(|p| p.fill(0));
|
|||
|
break;
|
|||
|
}
|
|||
|
RLEInsn::Delta(x_delta, y_delta) => {
|
|||
|
// 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.
|
|||
|
|
|||
|
if y_delta > 0 {
|
|||
|
// Zero out the remainder of the current row.
|
|||
|
pixel_iter.for_each(|p| p.fill(0));
|
|||
|
|
|||
|
// If any full rows are skipped, zero them out.
|
|||
|
for _ in 1..y_delta {
|
|||
|
let row = row_iter.next().ok_or(DecoderError::CorruptRleData)?;
|
|||
|
row.fill(0);
|
|||
|
}
|
|||
|
|
|||
|
// Set the pixel iterator to the start of the next row.
|
|||
|
pixel_iter = row_iter
|
|||
|
.next()
|
|||
|
.ok_or(DecoderError::CorruptRleData)?
|
|||
|
.chunks_mut(num_channels);
|
|||
|
|
|||
|
// Zero out the pixels up to the current point in the row.
|
|||
|
for _ in 0..x {
|
|||
|
pixel_iter
|
|||
|
.next()
|
|||
|
.ok_or(DecoderError::CorruptRleData)?
|
|||
|
.fill(0);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
for _ in 0..x_delta {
|
|||
|
let pixel = pixel_iter.next().ok_or(DecoderError::CorruptRleData)?;
|
|||
|
pixel.fill(0);
|
|||
|
}
|
|||
|
x += x_delta as usize;
|
|||
|
}
|
|||
|
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,
|
|||
|
) {
|
|||
|
return Err(DecoderError::CorruptRleData.into());
|
|||
|
}
|
|||
|
}
|
|||
|
ImageType::RLE4 => {
|
|||
|
if !set_4bit_pixel_run(
|
|||
|
&mut pixel_iter,
|
|||
|
p,
|
|||
|
indices.iter(),
|
|||
|
length as usize,
|
|||
|
) {
|
|||
|
return Err(DecoderError::CorruptRleData.into());
|
|||
|
}
|
|||
|
}
|
|||
|
_ => unreachable!(),
|
|||
|
}
|
|||
|
x += length as usize;
|
|||
|
}
|
|||
|
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,
|
|||
|
) {
|
|||
|
return Err(DecoderError::CorruptRleData.into());
|
|||
|
}
|
|||
|
}
|
|||
|
ImageType::RLE4 => {
|
|||
|
if !set_4bit_pixel_run(
|
|||
|
&mut pixel_iter,
|
|||
|
p,
|
|||
|
repeat(&palette_index),
|
|||
|
n_pixels as usize,
|
|||
|
) {
|
|||
|
return Err(DecoderError::CorruptRleData.into());
|
|||
|
}
|
|||
|
}
|
|||
|
_ => unreachable!(),
|
|||
|
}
|
|||
|
x += n_pixels as usize;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
Ok(())
|
|||
|
}
|
|||
|
|
|||
|
/// 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<()> {
|
|||
|
match self.image_type {
|
|||
|
ImageType::Palette => self.read_palettized_pixel_data(buf),
|
|||
|
ImageType::RGB16 => self.read_16_bit_pixel_data(buf, Some(&R5_G5_B5_COLOR_MASK)),
|
|||
|
ImageType::RGB24 => self.read_full_byte_pixel_data(buf, &FormatFullBytes::RGB24),
|
|||
|
ImageType::RGB32 => self.read_full_byte_pixel_data(buf, &FormatFullBytes::RGB32),
|
|||
|
ImageType::RGBA32 => self.read_full_byte_pixel_data(buf, &FormatFullBytes::RGBA32),
|
|||
|
ImageType::RLE8 => self.read_rle_data(buf, ImageType::RLE8),
|
|||
|
ImageType::RLE4 => self.read_rle_data(buf, ImageType::RLE4),
|
|||
|
ImageType::Bitfields16 => match self.bitfields {
|
|||
|
Some(_) => self.read_16_bit_pixel_data(buf, None),
|
|||
|
None => Err(DecoderError::BitfieldMasksMissing(16).into()),
|
|||
|
},
|
|||
|
ImageType::Bitfields32 => match self.bitfields {
|
|||
|
Some(R8_G8_B8_COLOR_MASK) => {
|
|||
|
self.read_full_byte_pixel_data(buf, &FormatFullBytes::Format888)
|
|||
|
}
|
|||
|
Some(R8_G8_B8_A8_COLOR_MASK) => {
|
|||
|
self.read_full_byte_pixel_data(buf, &FormatFullBytes::RGBA32)
|
|||
|
}
|
|||
|
Some(_) => self.read_32_bit_pixel_data(buf),
|
|||
|
None => Err(DecoderError::BitfieldMasksMissing(32).into()),
|
|||
|
},
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// 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.indexed_color {
|
|||
|
ColorType::L8
|
|||
|
} else 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> ImageDecoderRect<'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.stream_position()?;
|
|||
|
image::load_rect(
|
|||
|
x,
|
|||
|
y,
|
|||
|
width,
|
|||
|
height,
|
|||
|
buf,
|
|||
|
progress_callback,
|
|||
|
self,
|
|||
|
|_, _| Ok(()),
|
|||
|
|s, buf| s.read_image_data(buf),
|
|||
|
)?;
|
|||
|
self.reader.seek(SeekFrom::Start(start))?;
|
|||
|
Ok(())
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#[cfg(test)]
|
|||
|
mod test {
|
|||
|
use super::*;
|
|||
|
|
|||
|
#[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);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#[test]
|
|||
|
fn read_rect() {
|
|||
|
let f = std::fs::File::open("tests/images/bmp/images/Core_8_Bit.bmp").unwrap();
|
|||
|
let mut decoder = super::BmpDecoder::new(f).unwrap();
|
|||
|
|
|||
|
let mut buf: Vec<u8> = vec![0; 8 * 8 * 3];
|
|||
|
decoder.read_rect(0, 0, 8, 8, &mut *buf).unwrap();
|
|||
|
}
|
|||
|
|
|||
|
#[test]
|
|||
|
fn read_rle_too_short() {
|
|||
|
let data = vec![
|
|||
|
0x42, 0x4d, 0x04, 0xee, 0xfe, 0xff, 0xff, 0x10, 0xff, 0x00, 0x04, 0x00, 0x00, 0x00,
|
|||
|
0x7c, 0x00, 0x00, 0x00, 0x0c, 0x41, 0x00, 0x00, 0x07, 0x10, 0x00, 0x00, 0x01, 0x00,
|
|||
|
0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00,
|
|||
|
0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x21,
|
|||
|
0xff, 0x00, 0x66, 0x61, 0x72, 0x62, 0x66, 0x65, 0x6c, 0x64, 0x00, 0x00, 0x00, 0x00,
|
|||
|
0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|||
|
0xff, 0xd8, 0xff, 0x00, 0x00, 0x19, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|||
|
0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfa, 0xff, 0x00, 0x00, 0x00,
|
|||
|
0x00, 0x01, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00,
|
|||
|
0x00, 0x00, 0x00, 0x2d, 0x31, 0x31, 0x35, 0x36, 0x00, 0xff, 0x00, 0x00, 0x52, 0x3a,
|
|||
|
0x37, 0x30, 0x7e, 0x71, 0x63, 0x91, 0x5a, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
|
|||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|||
|
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x35, 0x37, 0x00, 0xff, 0x00, 0x00, 0x52,
|
|||
|
0x3a, 0x37, 0x30, 0x7e, 0x71, 0x63, 0x91, 0x5a, 0x04, 0x05, 0x3c, 0x00, 0x00, 0x11,
|
|||
|
0x00, 0x5d, 0x7a, 0x82, 0xb7, 0xca, 0x2d, 0x31, 0xff, 0xff, 0xc7, 0x95, 0x33, 0x2e,
|
|||
|
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00,
|
|||
|
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x66, 0x00, 0x4d,
|
|||
|
0x4d, 0x00, 0x2a, 0x00,
|
|||
|
];
|
|||
|
|
|||
|
let decoder = BmpDecoder::new(Cursor::new(&data)).unwrap();
|
|||
|
let mut buf = vec![0; usize::try_from(decoder.total_bytes()).unwrap()];
|
|||
|
assert!(decoder.read_image(&mut buf).is_ok());
|
|||
|
}
|
|||
|
|
|||
|
#[test]
|
|||
|
fn test_no_header() {
|
|||
|
let tests = [
|
|||
|
"Info_R8_G8_B8.bmp",
|
|||
|
"Info_A8_R8_G8_B8.bmp",
|
|||
|
"Info_8_Bit.bmp",
|
|||
|
"Info_4_Bit.bmp",
|
|||
|
"Info_1_Bit.bmp",
|
|||
|
];
|
|||
|
|
|||
|
for name in &tests {
|
|||
|
let path = format!("tests/images/bmp/images/{name}");
|
|||
|
let ref_img = crate::open(&path).unwrap();
|
|||
|
let mut data = std::fs::read(&path).unwrap();
|
|||
|
// skip the BITMAPFILEHEADER
|
|||
|
let slice = &mut data[14..];
|
|||
|
let decoder = BmpDecoder::new_without_file_header(Cursor::new(slice)).unwrap();
|
|||
|
let no_hdr_img = crate::DynamicImage::from_decoder(decoder).unwrap();
|
|||
|
assert_eq!(ref_img, no_hdr_img);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|