use crate::core::ImageFormat;
use crate::io::{IoError, IoResult, detect_format_from_bytes};
use std::fs::File;
use std::io::{BufReader, Read};
use std::path::Path;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ImageHeader {
pub width: u32,
pub height: u32,
pub depth: u32,
pub bps: u32,
pub spp: u32,
pub has_colormap: bool,
pub num_colors: u32,
pub format: ImageFormat,
pub x_resolution: Option<u32>,
pub y_resolution: Option<u32>,
}
pub fn read_image_header<P: AsRef<Path>>(path: P) -> IoResult<ImageHeader> {
let path = path.as_ref();
let file = File::open(path).map_err(IoError::Io)?;
let mut reader = BufReader::new(file);
let mut data = Vec::new();
reader.read_to_end(&mut data).map_err(IoError::Io)?;
read_image_header_mem(&data)
}
pub fn read_image_header_mem(data: &[u8]) -> IoResult<ImageHeader> {
let format = detect_format_from_bytes(data)?;
read_header_for_format(data, format)
}
fn read_header_for_format(data: &[u8], format: ImageFormat) -> IoResult<ImageHeader> {
match format {
#[cfg(feature = "bmp")]
ImageFormat::Bmp => crate::io::bmp::read_header_bmp(data),
#[cfg(feature = "pnm")]
ImageFormat::Pnm => crate::io::pnm::read_header_pnm(data),
#[cfg(feature = "png-format")]
ImageFormat::Png => crate::io::png::read_header_png(data),
#[cfg(feature = "jpeg")]
ImageFormat::Jpeg => crate::io::jpeg::read_header_jpeg(data),
#[cfg(feature = "tiff-format")]
ImageFormat::Tiff
| ImageFormat::TiffG3
| ImageFormat::TiffG4
| ImageFormat::TiffRle
| ImageFormat::TiffPackbits
| ImageFormat::TiffLzw
| ImageFormat::TiffZip
| ImageFormat::TiffJpeg => crate::io::tiff::read_header_tiff(data),
#[cfg(feature = "gif-format")]
ImageFormat::Gif => crate::io::gif::read_header_gif(data),
#[cfg(feature = "webp-format")]
ImageFormat::WebP => crate::io::webp::read_header_webp(data),
#[cfg(feature = "jp2k-format")]
ImageFormat::Jp2 => crate::io::jp2k::read_header_jp2k(data),
ImageFormat::Spix => crate::io::spix::read_header_spix(data),
_ => Err(IoError::UnsupportedFormat(format!("{:?}", format))),
}
}
pub fn choose_output_format(pix: &crate::core::Pix) -> ImageFormat {
use crate::core::PixelDepth;
match pix.depth() {
PixelDepth::Bit1 | PixelDepth::Bit2 | PixelDepth::Bit4 => ImageFormat::Png,
PixelDepth::Bit8 => ImageFormat::Png,
PixelDepth::Bit16 => ImageFormat::Png,
PixelDepth::Bit32 => {
if pix.spp() == 3 {
ImageFormat::Jpeg
} else {
ImageFormat::Png
}
}
}
}
pub fn write_image_auto<P: AsRef<Path>>(pix: &crate::core::Pix, path: P) -> IoResult<()> {
let path = path.as_ref();
let format = ImageFormat::from_path(path).unwrap_or_else(|| choose_output_format(pix));
crate::io::write_image(pix, path, format)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core::{ImageFormat, Pix, PixelDepth};
#[test]
fn test_from_extension_bmp() {
assert_eq!(ImageFormat::from_extension("bmp"), Some(ImageFormat::Bmp));
assert_eq!(ImageFormat::from_extension("BMP"), Some(ImageFormat::Bmp));
}
#[test]
fn test_from_extension_jpeg() {
assert_eq!(ImageFormat::from_extension("jpg"), Some(ImageFormat::Jpeg));
assert_eq!(ImageFormat::from_extension("jpeg"), Some(ImageFormat::Jpeg));
assert_eq!(ImageFormat::from_extension("JPG"), Some(ImageFormat::Jpeg));
}
#[test]
fn test_from_extension_png() {
assert_eq!(ImageFormat::from_extension("png"), Some(ImageFormat::Png));
}
#[test]
fn test_from_extension_tiff() {
assert_eq!(ImageFormat::from_extension("tif"), Some(ImageFormat::Tiff));
assert_eq!(ImageFormat::from_extension("tiff"), Some(ImageFormat::Tiff));
}
#[test]
fn test_from_extension_pnm() {
assert_eq!(ImageFormat::from_extension("pnm"), Some(ImageFormat::Pnm));
assert_eq!(ImageFormat::from_extension("pbm"), Some(ImageFormat::Pnm));
assert_eq!(ImageFormat::from_extension("pgm"), Some(ImageFormat::Pnm));
assert_eq!(ImageFormat::from_extension("ppm"), Some(ImageFormat::Pnm));
}
#[test]
fn test_from_extension_gif() {
assert_eq!(ImageFormat::from_extension("gif"), Some(ImageFormat::Gif));
}
#[test]
fn test_from_extension_jp2k() {
assert_eq!(ImageFormat::from_extension("jp2"), Some(ImageFormat::Jp2));
assert_eq!(ImageFormat::from_extension("j2k"), Some(ImageFormat::Jp2));
}
#[test]
fn test_from_extension_ps() {
assert_eq!(ImageFormat::from_extension("ps"), Some(ImageFormat::Ps));
}
#[test]
fn test_from_extension_pdf() {
assert_eq!(ImageFormat::from_extension("pdf"), Some(ImageFormat::Lpdf));
}
#[test]
fn test_from_extension_webp() {
assert_eq!(ImageFormat::from_extension("webp"), Some(ImageFormat::WebP));
}
#[test]
fn test_from_extension_spix() {
assert_eq!(ImageFormat::from_extension("spix"), Some(ImageFormat::Spix));
}
#[test]
fn test_from_extension_unknown() {
assert_eq!(ImageFormat::from_extension("xyz"), None);
assert_eq!(ImageFormat::from_extension(""), None);
}
#[test]
fn test_from_path() {
let path = std::path::Path::new("image.png");
assert_eq!(ImageFormat::from_path(path), Some(ImageFormat::Png));
let path = std::path::Path::new("/tmp/test.jpg");
assert_eq!(ImageFormat::from_path(path), Some(ImageFormat::Jpeg));
let path = std::path::Path::new("noext");
assert_eq!(ImageFormat::from_path(path), None);
}
#[test]
fn test_choose_output_format_1bpp() {
let pix = Pix::new(10, 10, PixelDepth::Bit1).unwrap();
assert_eq!(choose_output_format(&pix), ImageFormat::Png);
}
#[test]
fn test_choose_output_format_8bpp() {
let pix = Pix::new(10, 10, PixelDepth::Bit8).unwrap();
assert_eq!(choose_output_format(&pix), ImageFormat::Png);
}
#[test]
fn test_choose_output_format_32bpp_rgb() {
let pix = Pix::new(10, 10, PixelDepth::Bit32).unwrap();
let mut pix_mut = pix.try_into_mut().unwrap();
pix_mut.set_spp(3);
let pix: Pix = pix_mut.into();
assert_eq!(choose_output_format(&pix), ImageFormat::Jpeg);
}
#[test]
fn test_choose_output_format_32bpp_rgba() {
let pix = Pix::new(10, 10, PixelDepth::Bit32).unwrap();
let mut pix_mut = pix.try_into_mut().unwrap();
pix_mut.set_spp(4);
let pix: Pix = pix_mut.into();
assert_eq!(choose_output_format(&pix), ImageFormat::Png);
}
#[test]
fn test_read_header_png() {
use crate::core::PixelDepth;
let pix = Pix::new(100, 80, PixelDepth::Bit8).unwrap();
let data = crate::io::write_image_mem(&pix, ImageFormat::Png).unwrap();
let header = read_image_header_mem(&data).unwrap();
assert_eq!(header.width, 100);
assert_eq!(header.height, 80);
assert_eq!(header.depth, 8);
assert_eq!(header.format, ImageFormat::Png);
}
#[test]
fn test_read_header_jpeg() {
use crate::core::PixelDepth;
let pix = Pix::new(100, 80, PixelDepth::Bit8).unwrap();
let data = crate::io::write_image_mem(&pix, ImageFormat::Jpeg).unwrap();
let header = read_image_header_mem(&data).unwrap();
assert_eq!(header.width, 100);
assert_eq!(header.height, 80);
assert_eq!(header.format, ImageFormat::Jpeg);
}
#[test]
fn test_read_header_spix() {
use crate::core::PixelDepth;
let pix = Pix::new(50, 40, PixelDepth::Bit8).unwrap();
let data = crate::io::write_image_mem(&pix, ImageFormat::Spix).unwrap();
let header = read_image_header_mem(&data).unwrap();
assert_eq!(header.width, 50);
assert_eq!(header.height, 40);
assert_eq!(header.depth, 8);
assert_eq!(header.format, ImageFormat::Spix);
}
#[test]
fn test_read_header_bmp() {
use crate::core::PixelDepth;
let pix = Pix::new(30, 20, PixelDepth::Bit8).unwrap();
let data = crate::io::write_image_mem(&pix, ImageFormat::Bmp).unwrap();
let header = read_image_header_mem(&data).unwrap();
assert_eq!(header.width, 30);
assert_eq!(header.height, 20);
assert_eq!(header.format, ImageFormat::Bmp);
}
}