use core::ffi::c_void;
use windows::Win32::Graphics::{
Direct2D::{
Common::{D2D1_ALPHA_MODE, D2D1_ALPHA_MODE_PREMULTIPLIED, D2D1_ALPHA_MODE_STRAIGHT, D2D1_PIXEL_FORMAT, D2D_SIZE_U},
ID2D1Bitmap, D2D1_BITMAP_PROPERTIES,
},
Dxgi::Common::{DXGI_FORMAT, DXGI_FORMAT_A8_UNORM, DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM},
};
use super::{canvas::D2DCanvas, device::D2DDeviceContext};
use crate::{
base::Dimension,
bitmap::{BitmapBackend, BitmapPixelFormat},
error::GraphicsError,
geometry::{FSize, ISize},
};
const DEFAULT_DPI: f32 = 96.0;
#[derive(Clone, Debug)]
pub struct D2DBitmap {
bitmap: ID2D1Bitmap,
}
impl Dimension for D2DBitmap {
fn size(&self) -> FSize {
let size = unsafe { self.bitmap.GetSize() };
size.into()
}
fn pixel_size(&self) -> ISize {
let size = unsafe { self.bitmap.GetPixelSize() };
ISize::new(size.width as i32, size.height as i32)
}
}
impl BitmapBackend for D2DBitmap {
type DeviceContextType = D2DDeviceContext;
type CanvasType = D2DCanvas;
fn new(
_context: Option<&Self::DeviceContextType>,
width: usize,
height: usize,
pixel_format: BitmapPixelFormat,
canvas: &Self::CanvasType,
) -> Result<Self, GraphicsError> {
if width == 0 || height == 0 {
return Err(invalid_param_error!((width, height)));
}
let size = D2D_SIZE_U {
width: width as u32,
height: height as u32,
};
let pixel_format = Self::get_d2d_pixel_format(pixel_format)?;
let properties = D2D1_BITMAP_PROPERTIES {
pixelFormat: pixel_format,
dpiX: DEFAULT_DPI,
dpiY: DEFAULT_DPI,
};
let render_target = canvas.render_target();
let bitmap = unsafe { render_target.CreateBitmap(size, None, 0, &properties).map_err(|err| GraphicsError::CreationFailed(err.to_string()))? };
Ok(Self {
bitmap,
})
}
fn from_buffer(
_context: Option<&Self::DeviceContextType>,
buffer: &[u8],
pitch: usize,
width: usize,
height: usize,
pixel_format: BitmapPixelFormat,
canvas: &Self::CanvasType,
) -> Result<Self, GraphicsError> {
if pitch == 0 || width == 0 || height == 0 {
return Err(invalid_param_error!((pitch, width, height)));
}
if buffer.len() != pitch * height {
return Err(invalid_param_error!(buffer));
}
let size = D2D_SIZE_U {
width: width as u32,
height: height as u32,
};
let pixel_format = Self::get_d2d_pixel_format(pixel_format)?;
let properties = D2D1_BITMAP_PROPERTIES {
pixelFormat: pixel_format,
dpiX: DEFAULT_DPI,
dpiY: DEFAULT_DPI,
};
let render_target = canvas.render_target();
let buffer_ptr = buffer.as_ptr() as *const c_void;
let bitmap = unsafe {
render_target
.CreateBitmap(size, Some(buffer_ptr), pitch as u32, &properties)
.map_err(|err| GraphicsError::CreationFailed(err.to_string()))?
};
Ok(Self {
bitmap,
})
}
}
impl D2DBitmap {
pub(super) fn bitmap(&self) -> &ID2D1Bitmap {
&self.bitmap
}
fn get_d2d_pixel_format(pixel_format: BitmapPixelFormat) -> Result<D2D1_PIXEL_FORMAT, GraphicsError> {
let (dxgi_format, alpha_mode) = match pixel_format {
BitmapPixelFormat::RGBA32 => (DXGI_FORMAT_R8G8B8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED),
BitmapPixelFormat::BGRA32 => (DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED),
BitmapPixelFormat::A8 => (DXGI_FORMAT_A8_UNORM, D2D1_ALPHA_MODE_STRAIGHT),
_ => return Err(GraphicsError::Unsupported(pixel_format.to_string())),
};
Ok(D2D1_PIXEL_FORMAT {
format: dxgi_format,
alphaMode: alpha_mode,
})
}
}