use windows::Win32::Graphics::Direct2D::Common::*;
use windows::Win32::Graphics::Direct2D::*;
use windows::Win32::Graphics::DirectWrite::*;
use windows::Win32::Graphics::Imaging::*;
use windows::core::*;
use windows_numerics::Vector2;
use wintf::com::d2d::*;
use wintf::com::dwrite::*;
use wintf::com::wic::*;
use wintf::ecs::GraphicsCore;
use super::common::{test_asset_path, with_com_initialized};
fn make_core() -> GraphicsCore {
GraphicsCore::new().expect("GraphicsCore::new")
}
fn red() -> D2D1_COLOR_F {
D2D1_COLOR_F {
r: 1.0,
g: 0.0,
b: 0.0,
a: 1.0,
}
}
fn unit_rect() -> D2D_RECT_F {
D2D_RECT_F {
left: 0.0,
top: 0.0,
right: 32.0,
bottom: 32.0,
}
}
#[test]
fn factory_creates_path_and_rounded_rectangle_geometry() {
let core = make_core();
let factory = core.d2d_factory().expect("factory");
factory
.create_path_geometry()
.expect("create_path_geometry");
let rounded = D2D1_ROUNDED_RECT {
rect: unit_rect(),
radiusX: 4.0,
radiusY: 4.0,
};
factory
.create_rounded_rectangle_geometry(&rounded)
.expect("create_rounded_rectangle_geometry");
}
#[test]
fn device_creates_device_context_and_command_list() {
let core = make_core();
let device = core.d2d_device().expect("device");
device
.create_device_context(D2D1_DEVICE_CONTEXT_OPTIONS_NONE)
.expect("create_device_context");
device.create_command_list().expect("create_command_list");
}
#[test]
fn command_list_open_always_returns_e_notimpl() {
let core = make_core();
let device = core.d2d_device().expect("device");
let cl = device.create_command_list().expect("command list");
let result = cl.open();
assert_eq!(
result.expect_err("open should fail").code(),
windows::Win32::Foundation::E_NOTIMPL
);
}
#[test]
fn command_list_close_succeeds_once_then_fails() {
let core = make_core();
let device = core.d2d_device().expect("device");
let cl = device.create_command_list().expect("command list");
cl.close().expect("first close");
assert!(
cl.close().is_err(),
"second close should fail (wrong state)"
);
}
#[test]
fn device_context_wrappers_record_and_stream_into_rec_command_sink() {
with_com_initialized(|| {
let core = make_core();
let device = core.d2d_device().expect("device");
let dwrite = core.dwrite_factory().expect("dwrite factory");
let dc = device
.create_device_context(D2D1_DEVICE_CONTEXT_OPTIONS_NONE)
.expect("dc");
let cl = device.create_command_list().expect("command list");
unsafe { dc.SetTarget(&cl) };
let dc_factory: ID2D1Factory = unsafe { dc.GetFactory() }.expect("GetFactory");
let rounded = D2D1_ROUNDED_RECT {
rect: unit_rect(),
radiusX: 4.0,
radiusY: 4.0,
};
let geometry = dc_factory
.create_rounded_rectangle_geometry(&rounded)
.expect("geometry");
let bitmap = load_test_bitmap(&dc);
let format = dwrite
.create_text_format(
w!("Segoe UI"),
None,
DWRITE_FONT_WEIGHT_NORMAL,
DWRITE_FONT_STYLE_NORMAL,
DWRITE_FONT_STRETCH_NORMAL,
16.0,
w!("en-us"),
)
.expect("text format");
let layout = dwrite
.create_text_layout(w!("Layout"), &format, 200.0, 50.0)
.expect("text layout");
unsafe { dc.BeginDraw() };
let identity = windows_numerics::Matrix3x2::identity();
dc.set_transform(&identity);
dc.clear(Some(&red()));
dc.clear(None);
let brush = dc
.create_solid_color_brush(&red(), None)
.expect("create_solid_color_brush");
dc.fill_rectangle(&unit_rect(), &brush);
dc.fill_ellipse(
&D2D1_ELLIPSE {
point: Vector2 { X: 16.0, Y: 16.0 },
radiusX: 8.0,
radiusY: 8.0,
},
&brush,
);
dc.fill_geometry(&geometry, &brush);
dc.draw_bitmap(
&bitmap,
Some(&unit_rect()),
1.0,
D2D1_INTERPOLATION_MODE_LINEAR,
None,
None,
);
dc.draw_text(
&HSTRING::from("Hi"),
&format,
&unit_rect(),
&brush,
D2D1_DRAW_TEXT_OPTIONS_NONE,
DWRITE_MEASURING_MODE_NATURAL,
);
dc.draw_text_layout(
Vector2 { X: 0.0, Y: 0.0 },
&layout,
&brush,
D2D1_DRAW_TEXT_OPTIONS_NONE,
);
let empty_cl = device.create_command_list().expect("empty cl");
empty_cl.close().expect("close empty cl");
dc.draw_image(&empty_cl);
unsafe { dc.EndDraw(None, None) }.expect("EndDraw");
cl.close().expect("close");
let sink: ID2D1CommandSink5 = RecCommandSink::new().into();
unsafe { cl.Stream(&sink) }.expect("Stream into RecCommandSink");
});
}
#[test]
fn draw_text_with_empty_hstring_does_not_panic() {
let core = make_core();
let device = core.d2d_device().expect("device");
let dwrite = core.dwrite_factory().expect("dwrite factory");
let dc = device
.create_device_context(D2D1_DEVICE_CONTEXT_OPTIONS_NONE)
.expect("dc");
let cl = device.create_command_list().expect("command list");
unsafe { dc.SetTarget(&cl) };
let format = dwrite
.create_text_format(
w!("Segoe UI"),
None,
DWRITE_FONT_WEIGHT_NORMAL,
DWRITE_FONT_STYLE_NORMAL,
DWRITE_FONT_STRETCH_NORMAL,
16.0,
w!("en-us"),
)
.expect("text format");
unsafe { dc.BeginDraw() };
let brush = dc
.create_solid_color_brush(&red(), None)
.expect("create_solid_color_brush");
dc.draw_text(
&HSTRING::new(),
&format,
&unit_rect(),
&brush,
D2D1_DRAW_TEXT_OPTIONS_NONE,
DWRITE_MEASURING_MODE_NATURAL,
);
dc.draw_text(
&HSTRING::from(""),
&format,
&unit_rect(),
&brush,
D2D1_DRAW_TEXT_OPTIONS_NONE,
DWRITE_MEASURING_MODE_NATURAL,
);
unsafe { dc.EndDraw(None, None) }.expect("EndDraw");
cl.close().expect("close");
}
fn load_test_bitmap(dc: &ID2D1DeviceContext) -> ID2D1Bitmap1 {
let factory = wic_factory().expect("wic_factory");
let path_h = HSTRING::from(test_asset_path("test_8x8_rgba.png").as_os_str());
let decoder = factory
.create_decoder_from_filename(
&path_h,
None,
windows::Win32::Foundation::GENERIC_READ,
WICDecodeMetadataCacheOnDemand,
)
.expect("decoder");
let frame = decoder.frame(0).expect("frame");
let source: IWICBitmapSource = frame.cast().expect("cast");
let converter = factory.create_format_converter().expect("converter");
converter
.init(
&source,
&GUID_WICPixelFormat32bppPBGRA,
WICBitmapDitherTypeNone,
None,
0.0,
WICBitmapPaletteTypeCustom,
)
.expect("converter init");
dc.create_bitmap_from_wic_bitmap(&converter.cast::<IWICBitmapSource>().expect("cast"))
.expect("create_bitmap_from_wic_bitmap")
}
#[test]
fn create_bitmap_from_wic_bitmap_preserves_size() {
with_com_initialized(|| {
let core = make_core();
let dc = core.device_context().expect("dc");
let bitmap = load_test_bitmap(dc);
let size = unsafe { bitmap.GetPixelSize() };
assert_eq!((size.width, size.height), (8, 8));
});
}
#[test]
fn command_struct_drop_does_not_release_original_com_object() {
let core = make_core();
let dc = core.device_context().expect("dc");
let brush = dc.create_solid_color_brush(&red(), None).expect("brush");
let brush_base: ID2D1Brush = brush.cast().expect("cast to ID2D1Brush");
let cmd = FillRectangle::new(unit_rect(), &brush_base);
drop(cmd);
unsafe {
brush.SetOpacity(0.5);
assert_eq!(brush.GetOpacity(), 0.5);
}
}
#[test]
fn draw_line_with_none_stroke_style_stores_none() {
let core = make_core();
let dc = core.device_context().expect("dc");
let brush = dc.create_solid_color_brush(&red(), None).expect("brush");
let brush_base: ID2D1Brush = brush.cast().expect("cast");
let cmd = DrawLine::new(
Vector2 { X: 0.0, Y: 0.0 },
Vector2 { X: 10.0, Y: 10.0 },
&brush_base,
2.0,
None,
);
assert!(cmd.strokestyle.is_none());
assert_eq!(cmd.strokewidth, 2.0);
assert_eq!(cmd.point1.X, 10.0);
}