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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
use crate::*;
use std::path::{Path, PathBuf};
use windows::Win32::{Graphics::Imaging::*, System::SystemServices::*};
use windows::core::Interface;

#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[repr(u32)]
pub enum Interpolation {
    NearestNeighbor = D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR.0,
    Linear = D2D1_INTERPOLATION_MODE_LINEAR.0,
    Cubic = D2D1_INTERPOLATION_MODE_CUBIC.0,
    MultiSampleLinear = D2D1_INTERPOLATION_MODE_MULTI_SAMPLE_LINEAR.0,
    Anisotropic = D2D1_INTERPOLATION_MODE_ANISOTROPIC.0,
    HighQualityCubic = D2D1_INTERPOLATION_MODE_HIGH_QUALITY_CUBIC.0,
}

#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Image(ID2D1Bitmap1);

impl Image {
    #[inline]
    pub(crate) fn new(
        dc: &ID2D1DeviceContext,
        factory: &IWICImagingFactory,
        loader: impl ImageLoader,
    ) -> windows::core::Result<Self> {
        loader.load(dc, factory)
    }

    #[inline]
    pub(crate) fn draw(
        &self,
        dc: &ID2D1DeviceContext,
        dest_rect: Rect,
        src_rect: Option<Rect>,
        interpolation: Interpolation,
    ) {
        let dest = Inner(dest_rect).into();
        let src = src_rect.map(|src| Inner(src).into());
        unsafe {
            dc.DrawBitmap2(
                &self.0,
                &dest,
                1.0,
                D2D1_INTERPOLATION_MODE(interpolation as _),
                if let Some(src) = src.as_ref() {
                    src as *const _
                } else {
                    std::ptr::null()
                },
                std::ptr::null(),
            );
        }
    }

    #[inline]
    pub fn size(&self) -> gecl::Size<u32> {
        unsafe {
            let size = self.0.GetPixelSize();
            gecl::Size::new(size.width, size.height)
        }
    }
}

unsafe impl Send for Image {}
unsafe impl Sync for Image {}

pub trait ImageLoader {
    fn load(
        &self,
        dc: &ID2D1DeviceContext,
        factory: &IWICImagingFactory,
    ) -> windows::core::Result<Image>;
}

impl<'a> ImageLoader for &'a Path {
    fn load(
        &self,
        dc: &ID2D1DeviceContext,
        factory: &IWICImagingFactory,
    ) -> windows::core::Result<Image> {
        unsafe {
            let decoder = {
                factory.CreateDecoderFromFilename(
                    self.to_str().unwrap(),
                    std::ptr::null(),
                    GENERIC_READ,
                    WICDecodeMetadataCacheOnDemand,
                )?
            };
            let frame = decoder.GetFrame(0)?;
            let converter = {
                let converter = factory.CreateFormatConverter()?;
                let mut guid = GUID_WICPixelFormat32bppPBGRA.clone();
                converter.Initialize(
                    &frame,
                    &mut guid,
                    WICBitmapDitherTypeNone,
                    None,
                    1.0,
                    WICBitmapPaletteTypeMedianCut,
                )?;
                converter
            };
            let bitmap = {
                dc.CreateBitmapFromWicBitmap(&converter, std::ptr::null())?
                    .cast()?
            };
            Ok(Image(bitmap))
        }
    }
}

impl ImageLoader for PathBuf {
    fn load(
        &self,
        dc: &ID2D1DeviceContext,
        factory: &IWICImagingFactory,
    ) -> windows::core::Result<Image> {
        self.as_path().load(dc, factory)
    }
}

impl<'a> ImageLoader for &'a str {
    fn load(
        &self,
        dc: &ID2D1DeviceContext,
        factory: &IWICImagingFactory,
    ) -> windows::core::Result<Image> {
        Path::new(self).load(dc, factory)
    }
}

impl<'a> ImageLoader for &'a String {
    fn load(
        &self,
        dc: &ID2D1DeviceContext,
        factory: &IWICImagingFactory,
    ) -> windows::core::Result<Image> {
        Path::new(self).load(dc, factory)
    }
}