1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
//!DIB Image wrapper
use std::os::raw::{c_void};
use std::io::{self, Write};
use core::{slice};

use crate::utils::LockedData;

use lazy_bytes_cast::slice::AsByteSlice;

const HEADER_SIZE: u32 = 14;

#[repr(C)]
#[derive(Clone, Copy)]
struct BmpHeader {
    signature: u16,
    size: u32,
    reserved: u32,
    offset: u32,
}

#[repr(C)]
#[derive(Clone, Copy)]
struct Bmp {
    header: BmpHeader,
    dib: *const winapi::um::wingdi::BITMAPINFOHEADER
}

///Represents locked `DIB` clipboard data
pub struct Image {
    bmp: Bmp,
    _guard: LockedData,
}

impl Image {
    ///Constructs new image from clipboard data handle.
    pub(crate) fn from_handle(handle: *mut c_void) -> io::Result<Self> {
        let (data, _guard) = LockedData::new(handle)?;

        let dib = data.as_ptr();
        let mut bmp = Bmp {
            header: BmpHeader {
                signature: 0x4D42,
                size: 0,
                reserved: 0,
                offset: 0
            },
            dib,
        };

        bmp.header.offset = unsafe {
            HEADER_SIZE + (*dib).biSize
        };

        bmp.header.size = unsafe {
            (*bmp.dib).biSizeImage + bmp.header.offset
        };

        Ok(Self {
            bmp,
            _guard
        })
    }

    ///Access raw image data
    pub fn as_bytes(&self) -> &[u8] {
        let size = unsafe { winapi::um::winbase::GlobalSize(self._guard.0) };
        let data_ptr = self.bmp.dib as *const u8;

        unsafe {
            slice::from_raw_parts(data_ptr, size)
        }
    }

    #[inline]
    ///Retrieves image size, including header.
    pub fn size(&self) -> usize {
        self.bmp.header.size as usize
    }

    ///Writes data into storage.
    ///
    ///Returns size of written, if there is not enough space returns 0.
    pub fn write<W: Write>(&self, storage: &mut W) -> io::Result<usize> {
        //BMP Header
        storage.write_all(self.bmp.header.signature.as_slice())?;
        storage.write_all(self.bmp.header.size.as_slice())?;
        storage.write_all(self.bmp.header.reserved.as_slice())?;
        storage.write_all(self.bmp.header.offset.as_slice())?;

        //DIB Header
        let dib = unsafe {
            &*self.bmp.dib
        };
        storage.write_all(dib.biSize.as_slice())?;
        storage.write_all(dib.biWidth.as_slice())?;
        storage.write_all(dib.biHeight.as_slice())?;
        storage.write_all(dib.biPlanes.as_slice())?;
        storage.write_all(dib.biBitCount.as_slice())?;
        storage.write_all(dib.biCompression.as_slice())?;
        storage.write_all(dib.biSizeImage.as_slice())?;
        storage.write_all(dib.biXPelsPerMeter.as_slice())?;
        storage.write_all(dib.biYPelsPerMeter.as_slice())?;
        storage.write_all(dib.biClrUsed.as_slice())?;
        storage.write_all(dib.biClrImportant.as_slice())?;

        //Image data
        let image_data = unsafe {
            let image_ptr = self.bmp.dib.add(1) as *const u8;
            slice::from_raw_parts(image_ptr, dib.biSizeImage as usize)
        };
        storage.write_all(image_data)?;

        Ok(self.size())
    }

    ///Extracts BMP data as Vec.
    pub fn to_vec(&self) -> Vec<u8> {
        let mut data = Vec::with_capacity(self.size());
        let _ = self.write(&mut data);

        data
    }
}