#![allow(unused)]
use std::ffi::c_void;
use std::fmt::{Debug, Display, Formatter};
use std::marker::PhantomData;
use std::ops::Deref;
use std::ptr::{null, null_mut};
use piet::kurbo::{Circle, Line, Rect, RoundedRect};
use wio::com::ComPtr;
use winapi::shared::dxgi::{IDXGIDevice, IDXGISurface};
use winapi::shared::dxgiformat::DXGI_FORMAT_R8G8B8A8_UNORM;
use winapi::shared::minwindef::TRUE;
use winapi::shared::winerror::{HRESULT, SUCCEEDED};
use winapi::um::d2d1::{
D2D1CreateFactory, ID2D1Bitmap, ID2D1BitmapRenderTarget, ID2D1Brush, ID2D1EllipseGeometry,
ID2D1Geometry, ID2D1GeometrySink, ID2D1GradientStopCollection, ID2D1Image, ID2D1Layer,
ID2D1PathGeometry, ID2D1RectangleGeometry, ID2D1RenderTarget, ID2D1RoundedRectangleGeometry,
ID2D1SolidColorBrush, ID2D1StrokeStyle, D2D1_ANTIALIAS_MODE_PER_PRIMITIVE, D2D1_BEZIER_SEGMENT,
D2D1_BITMAP_INTERPOLATION_MODE, D2D1_BRUSH_PROPERTIES, D2D1_COLOR_F,
D2D1_COMPATIBLE_RENDER_TARGET_OPTIONS_NONE, D2D1_DEBUG_LEVEL_NONE, D2D1_DEBUG_LEVEL_WARNING,
D2D1_DRAW_TEXT_OPTIONS, D2D1_EXTEND_MODE_CLAMP, D2D1_FACTORY_OPTIONS,
D2D1_FACTORY_TYPE_MULTI_THREADED, D2D1_FIGURE_BEGIN_FILLED, D2D1_FIGURE_BEGIN_HOLLOW,
D2D1_FIGURE_END_CLOSED, D2D1_FIGURE_END_OPEN, D2D1_FILL_MODE_ALTERNATE, D2D1_FILL_MODE_WINDING,
D2D1_GAMMA_2_2, D2D1_GRADIENT_STOP, D2D1_LAYER_OPTIONS_NONE, D2D1_LAYER_PARAMETERS,
D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES, D2D1_MATRIX_3X2_F, D2D1_POINT_2F, D2D1_POINT_2U,
D2D1_QUADRATIC_BEZIER_SEGMENT, D2D1_RADIAL_GRADIENT_BRUSH_PROPERTIES, D2D1_RECT_F, D2D1_RECT_U,
D2D1_SIZE_F, D2D1_SIZE_U, D2D1_STROKE_STYLE_PROPERTIES,
};
use winapi::um::d2d1_1::{
ID2D1Bitmap1, ID2D1Device, ID2D1DeviceContext, ID2D1Effect, ID2D1Factory1,
D2D1_BITMAP_OPTIONS_NONE, D2D1_BITMAP_OPTIONS_TARGET, D2D1_BITMAP_PROPERTIES1,
D2D1_COMPOSITE_MODE, D2D1_DEVICE_CONTEXT_OPTIONS_NONE, D2D1_INTERPOLATION_MODE,
D2D1_PROPERTY_TYPE_FLOAT,
};
use winapi::um::d2d1_1::{D2D1_PRIMITIVE_BLEND_COPY, D2D1_PRIMITIVE_BLEND_SOURCE_OVER};
use winapi::um::d2d1effects::{CLSID_D2D1GaussianBlur, D2D1_GAUSSIANBLUR_PROP_STANDARD_DEVIATION};
use winapi::um::dcommon::{D2D1_ALPHA_MODE, D2D1_ALPHA_MODE_PREMULTIPLIED, D2D1_PIXEL_FORMAT};
use winapi::Interface;
use crate::conv::{circle_to_d2d, rect_to_rectf, rounded_rect_to_d2d, to_point2f};
use crate::dwrite::TextLayout;
pub enum FillRule {
EvenOdd,
NonZero,
}
pub enum Error {
WinapiError(HRESULT),
}
pub struct D2DFactory(ComPtr<ID2D1Factory1>);
pub struct D2DDevice(ComPtr<ID2D1Device>);
unsafe impl Send for D2DFactory {}
unsafe impl Send for D2DDevice {}
#[derive(Clone)]
pub struct DeviceContext(ComPtr<ID2D1DeviceContext>);
pub struct PathGeometry(ComPtr<ID2D1PathGeometry>);
pub struct RectangleGeometry(ComPtr<ID2D1RectangleGeometry>);
pub struct RoundedRectangleGeometry(ComPtr<ID2D1RoundedRectangleGeometry>);
pub struct EllipseGeometry(ComPtr<ID2D1EllipseGeometry>);
pub struct Geometry(ComPtr<ID2D1Geometry>);
pub struct GeometrySink<'a> {
ptr: ComPtr<ID2D1GeometrySink>,
marker: PhantomData<&'a mut PathGeometry>,
}
pub struct GradientStopCollection(ComPtr<ID2D1GradientStopCollection>);
pub struct SolidColorBrush(ComPtr<ID2D1SolidColorBrush>);
pub struct StrokeStyle(ComPtr<ID2D1StrokeStyle>);
pub struct Layer(ComPtr<ID2D1Layer>);
#[derive(Clone)]
pub struct Brush(ComPtr<ID2D1Brush>);
#[derive(Clone)]
pub struct Bitmap {
inner: ComPtr<ID2D1Bitmap1>,
pub(crate) empty_image: bool,
}
#[derive(Debug)]
pub struct Image {
inner: ComPtr<ID2D1Image>,
}
pub struct Effect(ComPtr<ID2D1Effect>);
pub struct BitmapRenderTarget(ComPtr<ID2D1BitmapRenderTarget>);
impl From<HRESULT> for Error {
fn from(hr: HRESULT) -> Error {
Error::WinapiError(hr)
}
}
impl Debug for Error {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
match self {
Error::WinapiError(hr) => write!(f, "hresult {hr:x}"),
}
}
}
impl Display for Error {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
match self {
Error::WinapiError(hr) => write!(f, "hresult {hr:x}"),
}
}
}
impl std::error::Error for Error {
fn description(&self) -> &str {
"winapi error"
}
}
impl From<Error> for piet::Error {
fn from(e: Error) -> piet::Error {
piet::Error::BackendError(Box::new(e))
}
}
impl From<PathGeometry> for Geometry {
fn from(pg: PathGeometry) -> Self {
Geometry(pg.0.up())
}
}
impl From<RectangleGeometry> for Geometry {
fn from(pg: RectangleGeometry) -> Self {
Geometry(pg.0.up())
}
}
impl From<RoundedRectangleGeometry> for Geometry {
fn from(pg: RoundedRectangleGeometry) -> Self {
Geometry(pg.0.up())
}
}
impl From<EllipseGeometry> for Geometry {
fn from(pg: EllipseGeometry) -> Self {
Geometry(pg.0.up())
}
}
unsafe fn wrap<T, U, F>(hr: HRESULT, ptr: *mut T, f: F) -> Result<U, Error>
where
F: Fn(ComPtr<T>) -> U,
T: Interface,
{
if SUCCEEDED(hr) {
Ok(f(ComPtr::from_raw(ptr)))
} else {
Err(hr.into())
}
}
pub(crate) fn wrap_unit(hr: HRESULT) -> Result<(), Error> {
if SUCCEEDED(hr) {
Ok(())
} else {
Err(hr.into())
}
}
fn optional<T>(val: &Option<T>) -> *const T {
val.as_ref().map(|x| x as *const T).unwrap_or(null())
}
fn stroke_style_to_d2d(style: Option<&StrokeStyle>) -> *mut ID2D1StrokeStyle {
style.map(|ss| ss.0.as_raw()).unwrap_or(null_mut())
}
impl D2DFactory {
pub fn new() -> Result<D2DFactory, Error> {
unsafe {
let mut ptr: *mut ID2D1Factory1 = null_mut();
let hr = D2D1CreateFactory(
D2D1_FACTORY_TYPE_MULTI_THREADED,
&ID2D1Factory1::uuidof(),
&D2D1_FACTORY_OPTIONS {
debugLevel: match cfg!(debug_assertions) {
true => D2D1_DEBUG_LEVEL_WARNING,
false => D2D1_DEBUG_LEVEL_NONE,
},
},
&mut ptr as *mut _ as *mut _,
);
wrap(hr, ptr, D2DFactory)
}
}
pub unsafe fn create_device(&self, dxgi_device: *mut IDXGIDevice) -> Result<D2DDevice, Error> {
let mut ptr = null_mut();
let hr = self.0.CreateDevice(dxgi_device, &mut ptr);
wrap(hr, ptr, D2DDevice)
}
pub fn get_raw(&self) -> *mut ID2D1Factory1 {
self.0.as_raw()
}
pub fn create_path_geometry(&self) -> Result<PathGeometry, Error> {
unsafe {
let mut ptr = null_mut();
let hr = self.0.deref().deref().CreatePathGeometry(&mut ptr);
wrap(hr, ptr, PathGeometry)
}
}
pub fn create_rect_geometry(&self, rect: Rect) -> Result<RectangleGeometry, Error> {
unsafe {
let mut ptr = null_mut();
let hr = self
.0
.deref()
.deref()
.CreateRectangleGeometry(&rect_to_rectf(rect), &mut ptr);
wrap(hr, ptr, RectangleGeometry)
}
}
pub fn create_round_rect_geometry(
&self,
rect: Rect,
radius: f64,
) -> Result<RoundedRectangleGeometry, Error> {
unsafe {
let mut ptr = null_mut();
let hr = self
.0
.deref()
.deref()
.CreateRoundedRectangleGeometry(&rounded_rect_to_d2d(rect, radius), &mut ptr);
wrap(hr, ptr, RoundedRectangleGeometry)
}
}
pub fn create_circle_geometry(&self, circle: Circle) -> Result<EllipseGeometry, Error> {
unsafe {
let mut ptr = null_mut();
let hr = self
.0
.deref()
.deref()
.CreateEllipseGeometry(&circle_to_d2d(circle), &mut ptr);
wrap(hr, ptr, EllipseGeometry)
}
}
pub fn create_stroke_style(
&self,
props: &D2D1_STROKE_STYLE_PROPERTIES,
dashes: Option<&[f32]>,
) -> Result<StrokeStyle, Error> {
unsafe {
let mut ptr = null_mut();
let dashes_len = dashes.map(|d| d.len()).unwrap_or(0);
assert!(dashes_len <= 0xffff_ffff);
let hr = self.0.deref().deref().CreateStrokeStyle(
props,
dashes.map(|d| d.as_ptr()).unwrap_or(null()),
dashes_len as u32,
&mut ptr,
);
wrap(hr, ptr, StrokeStyle)
}
}
}
impl D2DDevice {
pub fn create_device_context(&mut self) -> Result<DeviceContext, Error> {
unsafe {
let mut ptr = null_mut();
let options = D2D1_DEVICE_CONTEXT_OPTIONS_NONE;
let hr = self.0.CreateDeviceContext(options, &mut ptr);
wrap(hr, ptr, DeviceContext)
}
}
}
const IDENTITY_MATRIX_3X2_F: D2D1_MATRIX_3X2_F = D2D1_MATRIX_3X2_F {
matrix: [[1.0, 0.0], [0.0, 1.0], [0.0, 0.0]],
};
const DEFAULT_BRUSH_PROPERTIES: D2D1_BRUSH_PROPERTIES = D2D1_BRUSH_PROPERTIES {
opacity: 1.0,
transform: IDENTITY_MATRIX_3X2_F,
};
impl DeviceContext {
pub unsafe fn new(ptr: ComPtr<ID2D1DeviceContext>) -> DeviceContext {
DeviceContext(ptr)
}
pub fn get_raw(&self) -> *mut ID2D1DeviceContext {
self.0.as_raw()
}
pub fn get_comptr(&self) -> &ComPtr<ID2D1DeviceContext> {
&self.0
}
pub unsafe fn create_bitmap_from_dxgi(
&self,
dxgi: &ComPtr<IDXGISurface>,
dpi_scale: f32,
) -> Result<Bitmap, Error> {
let mut ptr = null_mut();
let props = D2D1_BITMAP_PROPERTIES1 {
pixelFormat: D2D1_PIXEL_FORMAT {
format: DXGI_FORMAT_R8G8B8A8_UNORM,
alphaMode: D2D1_ALPHA_MODE_PREMULTIPLIED,
},
dpiX: 96.0 * dpi_scale,
dpiY: 96.0 * dpi_scale,
bitmapOptions: D2D1_BITMAP_OPTIONS_TARGET,
colorContext: null_mut(),
};
let hr = self
.0
.CreateBitmapFromDxgiSurface(dxgi.as_raw(), &props, &mut ptr);
wrap(hr, ptr, |ptr| Bitmap {
inner: ptr,
empty_image: false,
})
}
pub fn set_target(&mut self, target: &Bitmap) {
assert!(!target.empty_image);
unsafe { self.0.SetTarget(target.inner.as_raw() as *mut ID2D1Image) }
}
pub fn set_dpi_scale(&mut self, dpi_scale: f32) {
unsafe {
self.0.SetDpi(96. * dpi_scale, 96. * dpi_scale);
}
}
pub fn get_dpi_scale(&self) -> (f32, f32) {
let mut dpi_x = 0.0f32;
let mut dpi_y = 0.0f32;
unsafe {
self.0.GetDpi(&mut dpi_x, &mut dpi_y);
}
(dpi_x / 96., dpi_y / 96.)
}
pub fn begin_draw(&mut self) {
unsafe {
self.0.BeginDraw();
}
}
pub fn end_draw(&mut self) -> Result<(), Error> {
unsafe {
let mut tag1 = 0;
let mut tag2 = 0;
let hr = self.0.EndDraw(&mut tag1, &mut tag2);
wrap_unit(hr)
}
}
pub(crate) fn push_axis_aligned_clip(&mut self, rect: Rect) {
unsafe {
self.0
.PushAxisAlignedClip(&rect_to_rectf(rect), D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
}
}
pub(crate) fn pop_axis_aligned_clip(&mut self) {
unsafe {
self.0.PopAxisAlignedClip();
}
}
pub(crate) fn clear(&mut self, color: D2D1_COLOR_F) {
unsafe {
self.0.Clear(&color);
}
}
pub(crate) fn set_transform(&mut self, transform: &D2D1_MATRIX_3X2_F) {
unsafe {
self.0.SetTransform(transform);
}
}
pub(crate) fn set_transform_identity(&mut self) {
unsafe {
self.0.SetTransform(&IDENTITY_MATRIX_3X2_F);
}
}
pub(crate) fn get_transform(&mut self) -> D2D1_MATRIX_3X2_F {
unsafe {
let mut empty = D2D1_MATRIX_3X2_F {
matrix: [[0.0, 0.0], [0.0, 0.0], [0.0, 0.0]],
};
self.0.GetTransform(&mut empty);
empty
}
}
pub(crate) fn fill_geometry(
&mut self,
geom: &Geometry,
brush: &Brush,
opacity_brush: Option<&Brush>,
) {
unsafe {
self.0.FillGeometry(
geom.0.as_raw(),
brush.as_raw(),
opacity_brush.map(|b| b.as_raw()).unwrap_or(null_mut()),
);
}
}
pub(crate) fn draw_geometry(
&mut self,
geom: &Geometry,
brush: &Brush,
width: f32,
style: Option<&StrokeStyle>,
) {
unsafe {
self.0.DrawGeometry(
geom.0.as_raw(),
brush.as_raw(),
width,
style.map(|ss| ss.0.as_raw()).unwrap_or(null_mut()),
);
}
}
pub(crate) fn draw_line(
&self,
line: Line,
brush: &Brush,
width: f32,
style: Option<&StrokeStyle>,
) {
unsafe {
self.0.DrawLine(
to_point2f(line.p0),
to_point2f(line.p1),
brush.as_raw(),
width,
stroke_style_to_d2d(style),
);
}
}
pub(crate) fn draw_rect(
&self,
rect: Rect,
brush: &Brush,
width: f32,
style: Option<&StrokeStyle>,
) {
unsafe {
self.0.DrawRectangle(
&rect_to_rectf(rect),
brush.as_raw(),
width,
stroke_style_to_d2d(style),
);
}
}
pub(crate) fn draw_rounded_rect(
&mut self,
rect: Rect,
radius: f64,
brush: &Brush,
width: f32,
style: Option<&StrokeStyle>,
) {
let d2d_rounded_rect = rounded_rect_to_d2d(rect, radius);
unsafe {
self.0.DrawRoundedRectangle(
&d2d_rounded_rect,
brush.as_raw(),
width,
stroke_style_to_d2d(style),
);
}
}
pub(crate) fn draw_circle(
&self,
circle: Circle,
brush: &Brush,
width: f32,
style: Option<&StrokeStyle>,
) {
unsafe {
self.0.DrawEllipse(
&circle_to_d2d(circle),
brush.as_raw(),
width,
stroke_style_to_d2d(style),
);
}
}
pub(crate) fn fill_rect(&self, rect: Rect, brush: &Brush) {
unsafe {
self.0.FillRectangle(&rect_to_rectf(rect), brush.as_raw());
}
}
pub(crate) fn fill_rounded_rect(&mut self, rect: Rect, radius: f64, brush: &Brush) {
let d2d_rounded_rect = rounded_rect_to_d2d(rect, radius);
unsafe {
self.0
.FillRoundedRectangle(&d2d_rounded_rect, brush.as_raw());
}
}
pub(crate) fn fill_circle(&self, circle: Circle, brush: &Brush) {
unsafe {
self.0.FillEllipse(&circle_to_d2d(circle), brush.as_raw());
}
}
pub(crate) fn create_layer(&mut self, size: Option<D2D1_SIZE_F>) -> Result<Layer, Error> {
unsafe {
let mut ptr = null_mut();
let hr = self.0.CreateLayer(optional(&size), &mut ptr);
wrap(hr, ptr, Layer)
}
}
pub(crate) fn push_layer_mask(&mut self, mask: &Geometry, layer: &Layer) {
unsafe {
let params = D2D1_LAYER_PARAMETERS {
contentBounds: D2D1_RECT_F {
left: std::f32::NEG_INFINITY,
top: std::f32::NEG_INFINITY,
right: std::f32::INFINITY,
bottom: std::f32::INFINITY,
},
geometricMask: mask.0.as_raw(),
maskAntialiasMode: D2D1_ANTIALIAS_MODE_PER_PRIMITIVE,
maskTransform: IDENTITY_MATRIX_3X2_F,
opacity: 1.0,
opacityBrush: null_mut(),
layerOptions: D2D1_LAYER_OPTIONS_NONE,
};
self.0.deref().deref().PushLayer(¶ms, layer.0.as_raw());
}
}
pub(crate) fn pop_layer(&mut self) {
unsafe {
self.0.PopLayer();
}
}
pub(crate) fn create_solid_color(&mut self, color: D2D1_COLOR_F) -> Result<Brush, Error> {
unsafe {
let mut ptr = null_mut();
let hr = self
.0
.CreateSolidColorBrush(&color, &DEFAULT_BRUSH_PROPERTIES, &mut ptr);
wrap(hr, ptr, |p| Brush(p.up()))
}
}
pub(crate) fn create_gradient_stops(
&mut self,
stops: &[D2D1_GRADIENT_STOP],
) -> Result<GradientStopCollection, Error> {
unsafe {
assert!(stops.len() <= 0xffff_ffff);
let mut ptr = null_mut();
let hr = self.0.deref().deref().CreateGradientStopCollection(
stops.as_ptr(),
stops.len() as u32,
D2D1_GAMMA_2_2,
D2D1_EXTEND_MODE_CLAMP,
&mut ptr,
);
wrap(hr, ptr, GradientStopCollection)
}
}
#[allow(clippy::trivially_copy_pass_by_ref)]
pub(crate) fn create_linear_gradient(
&mut self,
props: &D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES,
stops: &GradientStopCollection,
) -> Result<Brush, Error> {
unsafe {
let mut ptr = null_mut();
let hr = self.0.CreateLinearGradientBrush(
props,
&DEFAULT_BRUSH_PROPERTIES,
stops.0.as_raw(),
&mut ptr,
);
wrap(hr, ptr, |p| Brush(p.up()))
}
}
pub(crate) fn create_radial_gradient(
&mut self,
props: &D2D1_RADIAL_GRADIENT_BRUSH_PROPERTIES,
stops: &GradientStopCollection,
) -> Result<Brush, Error> {
unsafe {
let mut ptr = null_mut();
let hr = self.0.CreateRadialGradientBrush(
props,
&DEFAULT_BRUSH_PROPERTIES,
stops.0.as_raw(),
&mut ptr,
);
wrap(hr, ptr, |p| Brush(p.up()))
}
}
pub(crate) fn create_bitmap(
&mut self,
width: usize,
height: usize,
buf: &[u8],
alpha_mode: D2D1_ALPHA_MODE,
) -> Result<Bitmap, Error> {
assert!(width != 0 && width <= 0x3fff_ffff);
assert!(height != 0 && height <= 0xffff_ffff);
let size = D2D1_SIZE_U {
width: width as u32,
height: height as u32,
};
let format = D2D1_PIXEL_FORMAT {
format: DXGI_FORMAT_R8G8B8A8_UNORM,
alphaMode: alpha_mode,
};
let props = D2D1_BITMAP_PROPERTIES1 {
pixelFormat: format,
dpiX: 96.0,
dpiY: 96.0,
bitmapOptions: D2D1_BITMAP_OPTIONS_NONE,
colorContext: null_mut(),
};
let pitch = (width * 4) as u32;
unsafe {
let mut ptr = null_mut();
let hr = self.0.deref().CreateBitmap(
size,
buf.as_ptr() as *const c_void,
pitch,
&props,
&mut ptr,
);
wrap(hr, ptr, |ptr| Bitmap {
inner: ptr,
empty_image: false,
})
}
}
pub(crate) fn create_blank_bitmap(
&mut self,
width: usize,
height: usize,
dpi_scale: f32,
) -> Result<Bitmap, Error> {
assert!(width != 0 && width <= 0x3fff_ffff);
assert!(height != 0 && height <= 0xffff_ffff);
let size = D2D1_SIZE_U {
width: width as u32,
height: height as u32,
};
unsafe {
let pixel_format = self.0.GetPixelFormat();
let props = D2D1_BITMAP_PROPERTIES1 {
pixelFormat: pixel_format,
dpiX: dpi_scale * 96.0,
dpiY: dpi_scale * 96.0,
bitmapOptions: D2D1_BITMAP_OPTIONS_TARGET,
colorContext: null_mut(),
};
let mut ptr = null_mut();
let hr = self
.0
.deref()
.CreateBitmap(size, std::ptr::null(), 0, &props, &mut ptr);
wrap(hr, ptr, |ptr| Bitmap {
inner: ptr,
empty_image: false,
})
}
}
pub(crate) fn create_empty_bitmap(&mut self) -> Result<Bitmap, Error> {
unsafe {
let mut ptr = null_mut();
let hr = self.0.deref().CreateBitmap(
D2D1_SIZE_U {
width: 1,
height: 1,
},
[0, 0, 0, 0].as_ptr() as *const c_void,
4,
&D2D1_BITMAP_PROPERTIES1 {
pixelFormat: D2D1_PIXEL_FORMAT {
format: DXGI_FORMAT_R8G8B8A8_UNORM,
alphaMode: D2D1_ALPHA_MODE_PREMULTIPLIED,
},
dpiX: 96.0,
dpiY: 96.0,
bitmapOptions: D2D1_BITMAP_OPTIONS_NONE,
colorContext: null_mut(),
},
&mut ptr,
);
wrap(hr, ptr, |ptr| Bitmap {
inner: ptr,
empty_image: true,
})
}
}
pub(crate) fn draw_text_layout(
&mut self,
origin: D2D1_POINT_2F,
layout: &TextLayout,
brush: &Brush,
options: D2D1_DRAW_TEXT_OPTIONS,
) {
unsafe {
self.0
.DrawTextLayout(origin, layout.get_raw(), brush.as_raw(), options);
}
}
#[allow(clippy::trivially_copy_pass_by_ref)]
pub(crate) fn draw_bitmap(
&mut self,
bitmap: &Bitmap,
dst_rect: &D2D1_RECT_F,
opacity: f32,
interp_mode: D2D1_BITMAP_INTERPOLATION_MODE,
src_rect: Option<&D2D1_RECT_F>,
) {
unsafe {
self.0.deref().deref().DrawBitmap(
bitmap.inner.as_raw() as *mut ID2D1Bitmap,
dst_rect,
opacity,
interp_mode,
src_rect.map(|r| r as *const _).unwrap_or(null()),
);
}
}
pub(crate) fn create_blur_effect(&mut self, radius: f64) -> Result<Effect, Error> {
unsafe {
let mut ptr = null_mut();
let hr = self
.0
.deref()
.CreateEffect(&CLSID_D2D1GaussianBlur, &mut ptr);
let effect = wrap(hr, ptr, Effect)?;
let val = radius as f32;
let hr = effect.0.SetValue(
D2D1_GAUSSIANBLUR_PROP_STANDARD_DEVIATION,
D2D1_PROPERTY_TYPE_FLOAT,
&val as *const _ as *const _,
std::mem::size_of_val(&val) as u32,
);
wrap_unit(hr)?;
Ok(effect)
}
}
pub(crate) fn draw_image_effect(
&mut self,
effect: &Effect,
target_offset: Option<D2D1_POINT_2F>,
image_rect: Option<D2D1_RECT_F>,
interpolation_mode: D2D1_INTERPOLATION_MODE,
composite_mode: D2D1_COMPOSITE_MODE,
) {
unsafe {
let mut ptr = null_mut();
effect.0.GetOutput(&mut ptr);
let output = ComPtr::from_raw(ptr);
self.0.DrawImage(
output.as_raw(),
optional(&target_offset),
optional(&image_rect),
interpolation_mode,
composite_mode,
);
}
}
pub(crate) fn create_compatible_render_target(
&mut self,
width_f: f32,
height_f: f32,
) -> Result<BitmapRenderTarget, Error> {
unsafe {
let mut ptr = null_mut();
let size_f = D2D1_SIZE_F {
width: width_f,
height: height_f,
};
let format = D2D1_PIXEL_FORMAT {
format: DXGI_FORMAT_R8G8B8A8_UNORM,
alphaMode: D2D1_ALPHA_MODE_PREMULTIPLIED,
};
let options = D2D1_COMPATIBLE_RENDER_TARGET_OPTIONS_NONE;
let hr =
self.0
.CreateCompatibleRenderTarget(&size_f, null(), &format, options, &mut ptr);
wrap(hr, ptr, BitmapRenderTarget)
}
}
}
impl PathGeometry {
pub fn open(&mut self) -> Result<GeometrySink, Error> {
unsafe {
let mut ptr = null_mut();
let hr = (self.0).Open(&mut ptr);
wrap(hr, ptr, |ptr| GeometrySink {
ptr,
marker: Default::default(),
})
}
}
}
impl<'a> GeometrySink<'a> {
pub fn set_fill_mode(&mut self, fill_rule: FillRule) {
let fill_mode = match fill_rule {
FillRule::EvenOdd => D2D1_FILL_MODE_ALTERNATE,
FillRule::NonZero => D2D1_FILL_MODE_WINDING,
};
unsafe {
self.ptr.SetFillMode(fill_mode);
}
}
pub fn add_bezier(
&mut self,
point1: D2D1_POINT_2F,
point2: D2D1_POINT_2F,
point3: D2D1_POINT_2F,
) {
let seg = D2D1_BEZIER_SEGMENT {
point1,
point2,
point3,
};
unsafe {
self.ptr.AddBezier(&seg);
}
}
pub fn add_quadratic_bezier(&mut self, point1: D2D1_POINT_2F, point2: D2D1_POINT_2F) {
let seg = D2D1_QUADRATIC_BEZIER_SEGMENT { point1, point2 };
unsafe {
self.ptr.AddQuadraticBezier(&seg);
}
}
pub fn add_line(&mut self, point: D2D1_POINT_2F) {
unsafe {
self.ptr.AddLine(point);
}
}
pub fn begin_figure(&mut self, start: D2D1_POINT_2F, is_filled: bool) {
unsafe {
let figure_end = if is_filled {
D2D1_FIGURE_BEGIN_FILLED
} else {
D2D1_FIGURE_BEGIN_HOLLOW
};
self.ptr.BeginFigure(start, figure_end);
}
}
pub fn end_figure(&mut self, is_closed: bool) {
unsafe {
let figure_end = if is_closed {
D2D1_FIGURE_END_CLOSED
} else {
D2D1_FIGURE_END_OPEN
};
self.ptr.EndFigure(figure_end);
}
}
pub fn close(self) -> Result<(), Error> {
unsafe { wrap_unit(self.ptr.Close()) }
}
}
impl Bitmap {
pub fn get_size(&self) -> D2D1_SIZE_F {
unsafe { self.inner.GetSize() }
}
pub(crate) fn copy_from_render_target(
&mut self,
dest_point: D2D1_POINT_2U,
rt: &mut DeviceContext,
src_rect: D2D1_RECT_U,
) {
unsafe {
let rt = rt.get_raw() as *mut _;
self.inner.CopyFromRenderTarget(&dest_point, rt, &src_rect);
}
}
}
impl Effect {
pub(crate) fn set_input(&self, index: u32, input: &ID2D1Image) {
unsafe {
self.0.SetInput(index, input, TRUE);
}
}
}
impl BitmapRenderTarget {
pub(crate) fn get_bitmap(&self) -> Result<ComPtr<ID2D1Bitmap>, Error> {
unsafe {
let mut ptr = null_mut();
let hr = self.0.GetBitmap(&mut ptr);
wrap(hr, ptr, |com_ptr| com_ptr)
}
}
}
impl Deref for BitmapRenderTarget {
type Target = ID2D1BitmapRenderTarget;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Brush {
pub(crate) fn as_raw(&self) -> *mut ID2D1Brush {
self.0.as_raw()
}
}
mod tests {
use super::*;
#[test]
fn geom_builder() {
let mut factory = D2DFactory::new().unwrap();
let mut p = factory.create_path_geometry().unwrap();
let mut s1 = p.open().unwrap();
s1.close();
if let Ok(mut s2) = p.open() {
s2.close();
}
}
}