mod com;
use crate::com::ComLibrary;
use std::ffi::OsStr;
use std::mem::size_of;
use std::os::windows::ffi::OsStrExt;
use windows::core::{Interface, PCWSTR};
use windows::Win32::Foundation::SIZE;
use windows::Win32::Graphics::Gdi::{
CreateCompatibleDC, DeleteDC, DeleteObject, GetDIBits, GetObjectW, BITMAP, BITMAPFILEHEADER,
BITMAPINFO, BITMAPINFOHEADER, BI_RGB, DIB_RGB_COLORS, HGDIOBJ,
};
use windows::Win32::UI::Shell::{
IShellItem, IShellItemImageFactory, SHCreateItemFromParsingName, SIIGBF_THUMBNAILONLY,
};
thread_local! {
static COM_LIBRARY: ComLibrary = ComLibrary::init();
}
fn create_shell_item(file_name: &str) -> Result<IShellItem, windows::core::Error> {
let wide_file_name: Vec<u16> = OsStr::new(file_name).encode_wide().chain(Some(0)).collect();
unsafe { SHCreateItemFromParsingName(PCWSTR(wide_file_name.as_ptr()), None) }
}
#[derive(Debug)]
pub enum ThumbSize {
S16,
S32,
S48,
S96,
S256,
S768,
S1280,
S1920,
S2560,
Custom(i32, i32),
}
impl ThumbSize {
pub fn to_size(self) -> SIZE {
match self {
Self::S16 => SIZE { cx: 16, cy: 16 },
Self::S32 => SIZE { cx: 32, cy: 32 },
Self::S48 => SIZE { cx: 48, cy: 48 },
Self::S96 => SIZE { cx: 96, cy: 96 },
Self::S256 => SIZE { cx: 256, cy: 256 },
Self::S768 => SIZE { cx: 768, cy: 768 },
Self::S1280 => SIZE { cx: 1280, cy: 1280 },
Self::S1920 => SIZE { cx: 1920, cy: 1920 },
Self::S2560 => SIZE { cx: 2560, cy: 2560 },
Self::Custom(w, h) => SIZE { cx: w, cy: h },
}
}
}
pub fn get_bmp(file_path: &str, size: ThumbSize) -> Result<Vec<u8>, windows::core::Error> {
COM_LIBRARY.with(|_| {});
let hbitmap = unsafe {
let shell_item = create_shell_item(file_path)?;
let factory: IShellItemImageFactory = shell_item.cast()?;
factory.GetImage(size.to_size(), SIIGBF_THUMBNAILONLY)?
};
let hgdiobj: HGDIOBJ = hbitmap.into();
unsafe {
let mut bmp = BITMAP::default();
GetObjectW(
hgdiobj,
size_of::<BITMAP>() as i32,
Some(&mut bmp as *mut _ as *mut _),
);
let hdc = CreateCompatibleDC(None);
let mut bitmap_info = BITMAPINFO {
bmiHeader: BITMAPINFOHEADER {
biSize: size_of::<BITMAPINFOHEADER>() as u32,
biWidth: bmp.bmWidth,
biHeight: -bmp.bmHeight,
biPlanes: 1,
biBitCount: 32,
biCompression: BI_RGB.0,
..Default::default()
},
..Default::default()
};
let byte_size = 4 * bmp.bmWidth.abs() * bmp.bmHeight.abs();
let mut bits = vec![0u8; byte_size as usize];
let get_di_bits_result = GetDIBits(
hdc,
hbitmap,
0,
bmp.bmHeight.unsigned_abs(),
Some(bits.as_mut_ptr() as _),
&mut bitmap_info,
DIB_RGB_COLORS,
);
let _ = DeleteDC(hdc);
let _ = DeleteObject(hgdiobj);
if get_di_bits_result == 0 {
return Err(windows::core::Error::from_thread());
}
let bitmap_header_size = size_of::<BITMAPFILEHEADER>() + size_of::<BITMAPINFOHEADER>();
let bitmap_file_size = bitmap_header_size + bits.len();
let file_header = BITMAPFILEHEADER {
bfType: 0x4D42,
bfSize: bitmap_file_size as u32,
bfOffBits: bitmap_header_size as u32,
..Default::default()
};
let mut result = Vec::with_capacity(bitmap_file_size);
result.extend_from_slice(std::slice::from_raw_parts(
&file_header as *const _ as *const u8,
size_of::<BITMAPFILEHEADER>(),
));
result.extend_from_slice(std::slice::from_raw_parts(
&bitmap_info.bmiHeader as *const _ as *const u8,
size_of::<BITMAPINFOHEADER>(),
));
result.extend_from_slice(&bits);
Ok(result)
}
}