1#![allow(unused)]
11
12use std::ffi::c_void;
13use std::fmt::{Debug, Display, Formatter};
14use std::marker::PhantomData;
15use std::ops::Deref;
16use std::ptr::{null, null_mut};
17
18use piet::kurbo::{Circle, Line, Rect, RoundedRect};
19
20use wio::com::ComPtr;
21
22use winapi::Interface;
23use winapi::shared::dxgi::{IDXGIDevice, IDXGISurface};
24use winapi::shared::dxgiformat::DXGI_FORMAT_R8G8B8A8_UNORM;
25use winapi::shared::minwindef::TRUE;
26use winapi::shared::winerror::{HRESULT, SUCCEEDED};
27use winapi::um::d2d1::{
28 D2D1_ANTIALIAS_MODE_PER_PRIMITIVE, D2D1_BEZIER_SEGMENT, D2D1_BITMAP_INTERPOLATION_MODE,
29 D2D1_BRUSH_PROPERTIES, D2D1_COLOR_F, D2D1_COMPATIBLE_RENDER_TARGET_OPTIONS_NONE,
30 D2D1_DEBUG_LEVEL_NONE, D2D1_DEBUG_LEVEL_WARNING, D2D1_DRAW_TEXT_OPTIONS,
31 D2D1_EXTEND_MODE_CLAMP, D2D1_FACTORY_OPTIONS, D2D1_FACTORY_TYPE_MULTI_THREADED,
32 D2D1_FIGURE_BEGIN_FILLED, D2D1_FIGURE_BEGIN_HOLLOW, D2D1_FIGURE_END_CLOSED,
33 D2D1_FIGURE_END_OPEN, D2D1_FILL_MODE_ALTERNATE, D2D1_FILL_MODE_WINDING, D2D1_GAMMA_2_2,
34 D2D1_GRADIENT_STOP, D2D1_LAYER_OPTIONS_NONE, D2D1_LAYER_PARAMETERS,
35 D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES, D2D1_MATRIX_3X2_F, D2D1_POINT_2F, D2D1_POINT_2U,
36 D2D1_QUADRATIC_BEZIER_SEGMENT, D2D1_RADIAL_GRADIENT_BRUSH_PROPERTIES, D2D1_RECT_F, D2D1_RECT_U,
37 D2D1_SIZE_F, D2D1_SIZE_U, D2D1_STROKE_STYLE_PROPERTIES, D2D1CreateFactory, ID2D1Bitmap,
38 ID2D1BitmapRenderTarget, ID2D1Brush, ID2D1EllipseGeometry, ID2D1Geometry, ID2D1GeometrySink,
39 ID2D1GradientStopCollection, ID2D1Image, ID2D1Layer, ID2D1PathGeometry, ID2D1RectangleGeometry,
40 ID2D1RenderTarget, ID2D1RoundedRectangleGeometry, ID2D1SolidColorBrush, ID2D1StrokeStyle,
41};
42use winapi::um::d2d1_1::{
43 D2D1_BITMAP_OPTIONS_NONE, D2D1_BITMAP_OPTIONS_TARGET, D2D1_BITMAP_PROPERTIES1,
44 D2D1_COMPOSITE_MODE, D2D1_DEVICE_CONTEXT_OPTIONS_NONE, D2D1_INTERPOLATION_MODE,
45 D2D1_PROPERTY_TYPE_FLOAT, ID2D1Bitmap1, ID2D1Device, ID2D1DeviceContext, ID2D1Effect,
46 ID2D1Factory1,
47};
48use winapi::um::d2d1_1::{D2D1_PRIMITIVE_BLEND_COPY, D2D1_PRIMITIVE_BLEND_SOURCE_OVER};
49use winapi::um::d2d1effects::{CLSID_D2D1GaussianBlur, D2D1_GAUSSIANBLUR_PROP_STANDARD_DEVIATION};
50use winapi::um::dcommon::{D2D1_ALPHA_MODE, D2D1_ALPHA_MODE_PREMULTIPLIED, D2D1_PIXEL_FORMAT};
51
52use crate::conv::{circle_to_d2d, rect_to_rectf, rounded_rect_to_d2d, to_point2f};
53use crate::dwrite::TextLayout;
54
55pub enum FillRule {
56 EvenOdd,
57 NonZero,
58}
59
60pub enum Error {
61 WinapiError(HRESULT),
62}
63
64pub struct D2DFactory(ComPtr<ID2D1Factory1>);
69
70pub struct D2DDevice(ComPtr<ID2D1Device>);
72
73unsafe impl Send for D2DFactory {}
77unsafe impl Send for D2DDevice {}
78
79#[derive(Clone)]
87pub struct DeviceContext(ComPtr<ID2D1DeviceContext>);
88
89pub struct PathGeometry(ComPtr<ID2D1PathGeometry>);
90
91pub struct RectangleGeometry(ComPtr<ID2D1RectangleGeometry>);
92
93pub struct RoundedRectangleGeometry(ComPtr<ID2D1RoundedRectangleGeometry>);
94
95pub struct EllipseGeometry(ComPtr<ID2D1EllipseGeometry>);
96
97pub struct Geometry(ComPtr<ID2D1Geometry>);
98
99pub struct GeometrySink<'a> {
100 ptr: ComPtr<ID2D1GeometrySink>,
101 marker: PhantomData<&'a mut PathGeometry>,
106}
107
108pub struct GradientStopCollection(ComPtr<ID2D1GradientStopCollection>);
109
110pub struct SolidColorBrush(ComPtr<ID2D1SolidColorBrush>);
112
113pub struct StrokeStyle(ComPtr<ID2D1StrokeStyle>);
114
115pub struct Layer(ComPtr<ID2D1Layer>);
116
117#[derive(Clone)]
118pub struct Brush(ComPtr<ID2D1Brush>);
119
120#[derive(Clone)]
121pub struct Bitmap {
122 inner: ComPtr<ID2D1Bitmap1>,
123 pub(crate) empty_image: bool,
124}
125
126#[derive(Debug)]
127pub struct Image {
128 inner: ComPtr<ID2D1Image>,
129}
130
131pub struct Effect(ComPtr<ID2D1Effect>);
132
133pub struct BitmapRenderTarget(ComPtr<ID2D1BitmapRenderTarget>);
136
137pub struct DrawRestarter<'a> {
143 context: &'a mut DeviceContext,
144}
145
146impl<'a> Drop for DrawRestarter<'a> {
147 fn drop(&mut self) {
148 self.context.begin_draw();
149 }
150}
151
152impl From<HRESULT> for Error {
153 fn from(hr: HRESULT) -> Error {
154 Error::WinapiError(hr)
155 }
156}
157
158impl Debug for Error {
159 fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
160 match self {
161 Error::WinapiError(hr) => write!(f, "hresult {hr:x}"),
162 }
163 }
164}
165
166impl Display for Error {
167 fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
168 match self {
169 Error::WinapiError(hr) => write!(f, "hresult {hr:x}"),
170 }
171 }
172}
173
174impl std::error::Error for Error {
175 fn description(&self) -> &str {
176 "winapi error"
177 }
178}
179
180impl From<Error> for piet::Error {
181 fn from(e: Error) -> piet::Error {
182 piet::Error::BackendError(Box::new(e))
183 }
184}
185
186impl From<PathGeometry> for Geometry {
187 fn from(pg: PathGeometry) -> Self {
188 Geometry(pg.0.up())
189 }
190}
191
192impl From<RectangleGeometry> for Geometry {
193 fn from(pg: RectangleGeometry) -> Self {
194 Geometry(pg.0.up())
195 }
196}
197
198impl From<RoundedRectangleGeometry> for Geometry {
199 fn from(pg: RoundedRectangleGeometry) -> Self {
200 Geometry(pg.0.up())
201 }
202}
203
204impl From<EllipseGeometry> for Geometry {
205 fn from(pg: EllipseGeometry) -> Self {
206 Geometry(pg.0.up())
207 }
208}
209
210unsafe fn wrap<T, U, F>(hr: HRESULT, ptr: *mut T, f: F) -> Result<U, Error>
211where
212 F: Fn(ComPtr<T>) -> U,
213 T: Interface,
214{
215 if SUCCEEDED(hr) {
216 Ok(f(unsafe { ComPtr::from_raw(ptr) }))
217 } else {
218 Err(hr.into())
219 }
220}
221
222pub(crate) fn wrap_unit(hr: HRESULT) -> Result<(), Error> {
223 if SUCCEEDED(hr) {
224 Ok(())
225 } else {
226 Err(hr.into())
227 }
228}
229
230fn optional<T>(val: &Option<T>) -> *const T {
231 val.as_ref().map(|x| x as *const T).unwrap_or(null())
232}
233
234fn stroke_style_to_d2d(style: Option<&StrokeStyle>) -> *mut ID2D1StrokeStyle {
235 style.map(|ss| ss.0.as_raw()).unwrap_or(null_mut())
236}
237
238impl D2DFactory {
239 pub fn new() -> Result<D2DFactory, Error> {
244 unsafe {
245 let mut ptr: *mut ID2D1Factory1 = null_mut();
246 let hr = D2D1CreateFactory(
247 D2D1_FACTORY_TYPE_MULTI_THREADED,
248 &ID2D1Factory1::uuidof(),
249 &D2D1_FACTORY_OPTIONS {
250 debugLevel: match cfg!(debug_assertions) {
253 true => D2D1_DEBUG_LEVEL_WARNING,
254 false => D2D1_DEBUG_LEVEL_NONE,
255 },
256 },
257 &mut ptr as *mut _ as *mut _,
258 );
259 wrap(hr, ptr, D2DFactory)
260 }
261 }
262
263 pub unsafe fn create_device(&self, dxgi_device: *mut IDXGIDevice) -> Result<D2DDevice, Error> {
268 let mut ptr = null_mut();
269 unsafe {
270 let hr = self.0.CreateDevice(dxgi_device, &mut ptr);
271 wrap(hr, ptr, D2DDevice)
272 }
273 }
274
275 pub fn get_raw(&self) -> *mut ID2D1Factory1 {
277 self.0.as_raw()
278 }
279
280 pub fn create_path_geometry(&self) -> Result<PathGeometry, Error> {
281 unsafe {
282 let mut ptr = null_mut();
283 let hr = self.0.deref().deref().CreatePathGeometry(&mut ptr);
284 wrap(hr, ptr, PathGeometry)
285 }
286 }
287
288 pub fn create_rect_geometry(&self, rect: Rect) -> Result<RectangleGeometry, Error> {
289 unsafe {
290 let mut ptr = null_mut();
291 let hr = self
292 .0
293 .deref()
294 .deref()
295 .CreateRectangleGeometry(&rect_to_rectf(rect), &mut ptr);
296 wrap(hr, ptr, RectangleGeometry)
297 }
298 }
299
300 pub fn create_round_rect_geometry(
301 &self,
302 rect: Rect,
303 radius: f64,
304 ) -> Result<RoundedRectangleGeometry, Error> {
305 unsafe {
306 let mut ptr = null_mut();
307 let hr = self
308 .0
309 .deref()
310 .deref()
311 .CreateRoundedRectangleGeometry(&rounded_rect_to_d2d(rect, radius), &mut ptr);
312 wrap(hr, ptr, RoundedRectangleGeometry)
313 }
314 }
315
316 pub fn create_circle_geometry(&self, circle: Circle) -> Result<EllipseGeometry, Error> {
317 unsafe {
318 let mut ptr = null_mut();
319 let hr = self
320 .0
321 .deref()
322 .deref()
323 .CreateEllipseGeometry(&circle_to_d2d(circle), &mut ptr);
324 wrap(hr, ptr, EllipseGeometry)
325 }
326 }
327
328 pub fn create_stroke_style(
329 &self,
330 props: &D2D1_STROKE_STYLE_PROPERTIES,
331 dashes: Option<&[f32]>,
332 ) -> Result<StrokeStyle, Error> {
333 unsafe {
334 let mut ptr = null_mut();
335 let dashes_len = dashes.map(|d| d.len()).unwrap_or(0);
336 assert!(dashes_len <= 0xffff_ffff);
337 let hr = self.0.deref().deref().CreateStrokeStyle(
338 props,
339 dashes.map(|d| d.as_ptr()).unwrap_or(null()),
340 dashes_len as u32,
341 &mut ptr,
342 );
343 wrap(hr, ptr, StrokeStyle)
344 }
345 }
346}
347
348impl D2DDevice {
349 pub fn create_device_context(&mut self) -> Result<DeviceContext, Error> {
354 unsafe {
355 let mut ptr = null_mut();
356 let options = D2D1_DEVICE_CONTEXT_OPTIONS_NONE;
357 let hr = self.0.CreateDeviceContext(options, &mut ptr);
358 wrap(hr, ptr, DeviceContext)
359 }
360 }
361}
362
363const IDENTITY_MATRIX_3X2_F: D2D1_MATRIX_3X2_F = D2D1_MATRIX_3X2_F {
364 matrix: [[1.0, 0.0], [0.0, 1.0], [0.0, 0.0]],
365};
366
367const DEFAULT_BRUSH_PROPERTIES: D2D1_BRUSH_PROPERTIES = D2D1_BRUSH_PROPERTIES {
368 opacity: 1.0,
369 transform: IDENTITY_MATRIX_3X2_F,
370};
371
372impl DeviceContext {
373 pub unsafe fn new(ptr: ComPtr<ID2D1DeviceContext>) -> DeviceContext {
381 DeviceContext(ptr)
382 }
383
384 pub fn get_raw(&self) -> *mut ID2D1DeviceContext {
386 self.0.as_raw()
387 }
388
389 pub fn get_comptr(&self) -> &ComPtr<ID2D1DeviceContext> {
392 &self.0
393 }
394
395 pub unsafe fn create_bitmap_from_dxgi(
408 &self,
409 dxgi: &ComPtr<IDXGISurface>,
410 dpi_scale: f32,
411 ) -> Result<Bitmap, Error> {
412 let mut ptr = null_mut();
413 let props = D2D1_BITMAP_PROPERTIES1 {
414 pixelFormat: D2D1_PIXEL_FORMAT {
415 format: DXGI_FORMAT_R8G8B8A8_UNORM,
416 alphaMode: D2D1_ALPHA_MODE_PREMULTIPLIED,
417 },
418 dpiX: 96.0 * dpi_scale,
419 dpiY: 96.0 * dpi_scale,
420 bitmapOptions: D2D1_BITMAP_OPTIONS_TARGET,
421 colorContext: null_mut(),
422 };
423 unsafe {
424 let hr = self
425 .0
426 .CreateBitmapFromDxgiSurface(dxgi.as_raw(), &props, &mut ptr);
427 wrap(hr, ptr, |ptr| Bitmap {
428 inner: ptr,
429 empty_image: false,
432 })
433 }
434 }
435
436 pub fn set_target(&mut self, target: &Bitmap) {
440 assert!(!target.empty_image);
441 unsafe { self.0.SetTarget(target.inner.as_raw() as *mut ID2D1Image) }
442 }
443
444 pub fn set_dpi_scale(&mut self, dpi_scale: f32) {
448 unsafe {
449 self.0.SetDpi(96. * dpi_scale, 96. * dpi_scale);
450 }
451 }
452
453 pub fn get_dpi_scale(&self) -> (f32, f32) {
454 let mut dpi_x = 0.0f32;
455 let mut dpi_y = 0.0f32;
456 unsafe {
457 self.0.GetDpi(&mut dpi_x, &mut dpi_y);
458 }
459 (dpi_x / 96., dpi_y / 96.)
461 }
462
463 pub fn begin_draw(&mut self) {
470 unsafe {
471 self.0.BeginDraw();
472 }
473 }
474
475 pub fn end_draw(&mut self) -> Result<(), Error> {
477 unsafe {
478 let mut tag1 = 0;
479 let mut tag2 = 0;
480 let hr = self.0.EndDraw(&mut tag1, &mut tag2);
481 wrap_unit(hr)
482 }
483 }
484
485 pub fn end_draw_temporarily(&mut self) -> Result<DrawRestarter<'_>, Error> {
487 self.end_draw()?;
488 Ok(DrawRestarter { context: self })
489 }
490
491 pub(crate) fn push_axis_aligned_clip(&mut self, rect: Rect) {
497 unsafe {
498 self.0
499 .PushAxisAlignedClip(&rect_to_rectf(rect), D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
500 }
501 }
502
503 pub(crate) fn pop_axis_aligned_clip(&mut self) {
507 unsafe {
508 self.0.PopAxisAlignedClip();
509 }
510 }
511
512 pub(crate) fn clear(&mut self, color: D2D1_COLOR_F) {
513 unsafe {
514 self.0.Clear(&color);
515 }
516 }
517
518 pub(crate) fn set_transform(&mut self, transform: &D2D1_MATRIX_3X2_F) {
519 unsafe {
520 self.0.SetTransform(transform);
521 }
522 }
523
524 pub(crate) fn set_transform_identity(&mut self) {
525 unsafe {
526 self.0.SetTransform(&IDENTITY_MATRIX_3X2_F);
527 }
528 }
529
530 pub(crate) fn get_transform(&mut self) -> D2D1_MATRIX_3X2_F {
531 unsafe {
532 let mut empty = D2D1_MATRIX_3X2_F {
533 matrix: [[0.0, 0.0], [0.0, 0.0], [0.0, 0.0]],
534 };
535 self.0.GetTransform(&mut empty);
536 empty
537 }
538 }
539
540 pub(crate) fn fill_geometry(
541 &mut self,
542 geom: &Geometry,
543 brush: &Brush,
544 opacity_brush: Option<&Brush>,
545 ) {
546 unsafe {
547 self.0.FillGeometry(
548 geom.0.as_raw(),
549 brush.as_raw(),
550 opacity_brush.map(|b| b.as_raw()).unwrap_or(null_mut()),
551 );
552 }
553 }
554
555 pub(crate) fn draw_geometry(
556 &mut self,
557 geom: &Geometry,
558 brush: &Brush,
559 width: f32,
560 style: Option<&StrokeStyle>,
561 ) {
562 unsafe {
563 self.0.DrawGeometry(
564 geom.0.as_raw(),
565 brush.as_raw(),
566 width,
567 style.map(|ss| ss.0.as_raw()).unwrap_or(null_mut()),
568 );
569 }
570 }
571
572 pub(crate) fn draw_line(
573 &self,
574 line: Line,
575 brush: &Brush,
576 width: f32,
577 style: Option<&StrokeStyle>,
578 ) {
579 unsafe {
580 self.0.DrawLine(
581 to_point2f(line.p0),
582 to_point2f(line.p1),
583 brush.as_raw(),
584 width,
585 stroke_style_to_d2d(style),
586 );
587 }
588 }
589
590 pub(crate) fn draw_rect(
591 &self,
592 rect: Rect,
593 brush: &Brush,
594 width: f32,
595 style: Option<&StrokeStyle>,
596 ) {
597 unsafe {
598 self.0.DrawRectangle(
599 &rect_to_rectf(rect),
600 brush.as_raw(),
601 width,
602 stroke_style_to_d2d(style),
603 );
604 }
605 }
606
607 pub(crate) fn draw_rounded_rect(
608 &mut self,
609 rect: Rect,
610 radius: f64,
611 brush: &Brush,
612 width: f32,
613 style: Option<&StrokeStyle>,
614 ) {
615 let d2d_rounded_rect = rounded_rect_to_d2d(rect, radius);
616 unsafe {
617 self.0.DrawRoundedRectangle(
618 &d2d_rounded_rect,
619 brush.as_raw(),
620 width,
621 stroke_style_to_d2d(style),
622 );
623 }
624 }
625
626 pub(crate) fn draw_circle(
627 &self,
628 circle: Circle,
629 brush: &Brush,
630 width: f32,
631 style: Option<&StrokeStyle>,
632 ) {
633 unsafe {
634 self.0.DrawEllipse(
635 &circle_to_d2d(circle),
636 brush.as_raw(),
637 width,
638 stroke_style_to_d2d(style),
639 );
640 }
641 }
642
643 pub(crate) fn fill_rect(&self, rect: Rect, brush: &Brush) {
644 unsafe {
645 self.0.FillRectangle(&rect_to_rectf(rect), brush.as_raw());
646 }
647 }
648
649 pub(crate) fn fill_rounded_rect(&mut self, rect: Rect, radius: f64, brush: &Brush) {
650 let d2d_rounded_rect = rounded_rect_to_d2d(rect, radius);
651 unsafe {
652 self.0
653 .FillRoundedRectangle(&d2d_rounded_rect, brush.as_raw());
654 }
655 }
656
657 pub(crate) fn fill_circle(&self, circle: Circle, brush: &Brush) {
658 unsafe {
659 self.0.FillEllipse(&circle_to_d2d(circle), brush.as_raw());
660 }
661 }
662
663 pub(crate) fn create_layer(&mut self, size: Option<D2D1_SIZE_F>) -> Result<Layer, Error> {
664 unsafe {
665 let mut ptr = null_mut();
666 let hr = self.0.CreateLayer(optional(&size), &mut ptr);
667 wrap(hr, ptr, Layer)
668 }
669 }
670
671 pub(crate) fn push_layer_mask(&mut self, mask: &Geometry, layer: &Layer) {
673 unsafe {
674 let params = D2D1_LAYER_PARAMETERS {
675 contentBounds: D2D1_RECT_F {
676 left: f32::NEG_INFINITY,
677 top: f32::NEG_INFINITY,
678 right: f32::INFINITY,
679 bottom: f32::INFINITY,
680 },
681 geometricMask: mask.0.as_raw(),
682 maskAntialiasMode: D2D1_ANTIALIAS_MODE_PER_PRIMITIVE,
683 maskTransform: IDENTITY_MATRIX_3X2_F,
684 opacity: 1.0,
685 opacityBrush: null_mut(),
686 layerOptions: D2D1_LAYER_OPTIONS_NONE,
687 };
688 self.0.deref().deref().PushLayer(¶ms, layer.0.as_raw());
689 }
690 }
691
692 pub(crate) fn pop_layer(&mut self) {
693 unsafe {
694 self.0.PopLayer();
695 }
696 }
697
698 pub(crate) fn create_solid_color(&mut self, color: D2D1_COLOR_F) -> Result<Brush, Error> {
701 unsafe {
702 let mut ptr = null_mut();
703 let hr = self
704 .0
705 .CreateSolidColorBrush(&color, &DEFAULT_BRUSH_PROPERTIES, &mut ptr);
706 wrap(hr, ptr, |p| Brush(p.up()))
707 }
708 }
709
710 pub(crate) fn create_gradient_stops(
711 &mut self,
712 stops: &[D2D1_GRADIENT_STOP],
713 ) -> Result<GradientStopCollection, Error> {
714 unsafe {
715 assert!(stops.len() <= 0xffff_ffff);
718 let mut ptr = null_mut();
719 let hr = self.0.deref().deref().CreateGradientStopCollection(
722 stops.as_ptr(),
723 stops.len() as u32,
724 D2D1_GAMMA_2_2,
725 D2D1_EXTEND_MODE_CLAMP,
726 &mut ptr,
727 );
728 wrap(hr, ptr, GradientStopCollection)
729 }
730 }
731
732 #[allow(clippy::trivially_copy_pass_by_ref)]
733 pub(crate) fn create_linear_gradient(
734 &mut self,
735 props: &D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES,
736 stops: &GradientStopCollection,
737 ) -> Result<Brush, Error> {
738 unsafe {
739 let mut ptr = null_mut();
740 let hr = self.0.CreateLinearGradientBrush(
741 props,
742 &DEFAULT_BRUSH_PROPERTIES,
743 stops.0.as_raw(),
744 &mut ptr,
745 );
746 wrap(hr, ptr, |p| Brush(p.up()))
747 }
748 }
749
750 pub(crate) fn create_radial_gradient(
751 &mut self,
752 props: &D2D1_RADIAL_GRADIENT_BRUSH_PROPERTIES,
753 stops: &GradientStopCollection,
754 ) -> Result<Brush, Error> {
755 unsafe {
756 let mut ptr = null_mut();
757 let hr = self.0.CreateRadialGradientBrush(
758 props,
759 &DEFAULT_BRUSH_PROPERTIES,
760 stops.0.as_raw(),
761 &mut ptr,
762 );
763 wrap(hr, ptr, |p| Brush(p.up()))
764 }
765 }
766
767 pub(crate) fn create_bitmap(
769 &mut self,
770 width: usize,
771 height: usize,
772 buf: &[u8],
773 alpha_mode: D2D1_ALPHA_MODE,
774 ) -> Result<Bitmap, Error> {
775 assert!(width != 0 && width <= 0x3fff_ffff);
778 assert!(height != 0 && height <= 0xffff_ffff);
779 let size = D2D1_SIZE_U {
780 width: width as u32,
781 height: height as u32,
782 };
783 let format = D2D1_PIXEL_FORMAT {
784 format: DXGI_FORMAT_R8G8B8A8_UNORM,
785 alphaMode: alpha_mode,
786 };
787 let props = D2D1_BITMAP_PROPERTIES1 {
788 pixelFormat: format,
789 dpiX: 96.0,
790 dpiY: 96.0,
791 bitmapOptions: D2D1_BITMAP_OPTIONS_NONE,
792 colorContext: null_mut(),
793 };
794 let pitch = (width * 4) as u32;
795 unsafe {
796 let mut ptr = null_mut();
797 let hr = self.0.deref().CreateBitmap(
798 size,
799 buf.as_ptr() as *const c_void,
800 pitch,
801 &props,
802 &mut ptr,
803 );
804 wrap(hr, ptr, |ptr| Bitmap {
805 inner: ptr,
806 empty_image: false,
807 })
808 }
809 }
810
811 pub(crate) fn create_blank_bitmap(
812 &mut self,
813 width: usize,
814 height: usize,
815 dpi_scale: f32,
816 ) -> Result<Bitmap, Error> {
817 assert!(width != 0 && width <= 0x3fff_ffff);
820 assert!(height != 0 && height <= 0xffff_ffff);
821 let size = D2D1_SIZE_U {
822 width: width as u32,
823 height: height as u32,
824 };
825
826 unsafe {
827 let pixel_format = self.0.GetPixelFormat();
828 let props = D2D1_BITMAP_PROPERTIES1 {
829 pixelFormat: pixel_format,
830 dpiX: dpi_scale * 96.0,
831 dpiY: dpi_scale * 96.0,
832 bitmapOptions: D2D1_BITMAP_OPTIONS_TARGET,
833 colorContext: null_mut(),
834 };
835
836 let mut ptr = null_mut();
837 let hr = self
838 .0
839 .deref()
840 .CreateBitmap(size, std::ptr::null(), 0, &props, &mut ptr);
841 wrap(hr, ptr, |ptr| Bitmap {
842 inner: ptr,
843 empty_image: false,
844 })
845 }
846 }
847
848 pub(crate) fn create_empty_bitmap(&mut self) -> Result<Bitmap, Error> {
853 unsafe {
854 let mut ptr = null_mut();
855 let hr = self.0.deref().CreateBitmap(
856 D2D1_SIZE_U {
857 width: 1,
858 height: 1,
859 },
860 [0, 0, 0, 0].as_ptr() as *const c_void,
861 4,
862 &D2D1_BITMAP_PROPERTIES1 {
863 pixelFormat: D2D1_PIXEL_FORMAT {
864 format: DXGI_FORMAT_R8G8B8A8_UNORM,
865 alphaMode: D2D1_ALPHA_MODE_PREMULTIPLIED,
866 },
867 dpiX: 96.0,
868 dpiY: 96.0,
869 bitmapOptions: D2D1_BITMAP_OPTIONS_NONE,
870 colorContext: null_mut(),
871 },
872 &mut ptr,
873 );
874 wrap(hr, ptr, |ptr| Bitmap {
875 inner: ptr,
876 empty_image: true,
877 })
878 }
879 }
880
881 pub(crate) fn draw_text_layout(
882 &mut self,
883 origin: D2D1_POINT_2F,
884 layout: &TextLayout,
885 brush: &Brush,
886 options: D2D1_DRAW_TEXT_OPTIONS,
887 ) {
888 unsafe {
889 self.0
890 .DrawTextLayout(origin, layout.get_raw(), brush.as_raw(), options);
891 }
892 }
893
894 #[allow(clippy::trivially_copy_pass_by_ref)]
895 pub(crate) fn draw_bitmap(
896 &mut self,
897 bitmap: &Bitmap,
898 dst_rect: &D2D1_RECT_F,
899 opacity: f32,
900 interp_mode: D2D1_BITMAP_INTERPOLATION_MODE,
901 src_rect: Option<&D2D1_RECT_F>,
902 ) {
903 unsafe {
904 self.0.deref().deref().DrawBitmap(
907 bitmap.inner.as_raw() as *mut ID2D1Bitmap,
908 dst_rect,
909 opacity,
910 interp_mode,
911 src_rect.map(|r| r as *const _).unwrap_or(null()),
912 );
913 }
914 }
915
916 pub(crate) fn create_blur_effect(&mut self, radius: f64) -> Result<Effect, Error> {
918 unsafe {
919 let mut ptr = null_mut();
920 let hr = self
921 .0
922 .deref()
923 .CreateEffect(&CLSID_D2D1GaussianBlur, &mut ptr);
924 let effect = wrap(hr, ptr, Effect)?;
925 let val = radius as f32;
926 let hr = effect.0.SetValue(
927 D2D1_GAUSSIANBLUR_PROP_STANDARD_DEVIATION,
928 D2D1_PROPERTY_TYPE_FLOAT,
929 &val as *const _ as *const _,
930 std::mem::size_of_val(&val) as u32,
931 );
932 wrap_unit(hr)?;
933 Ok(effect)
934 }
935 }
936
937 pub(crate) fn draw_image_effect(
940 &mut self,
941 effect: &Effect,
942 target_offset: Option<D2D1_POINT_2F>,
943 image_rect: Option<D2D1_RECT_F>,
944 interpolation_mode: D2D1_INTERPOLATION_MODE,
945 composite_mode: D2D1_COMPOSITE_MODE,
946 ) {
947 unsafe {
948 let mut ptr = null_mut();
949 effect.0.GetOutput(&mut ptr);
950 let output = ComPtr::from_raw(ptr);
951 self.0.DrawImage(
952 output.as_raw(),
953 optional(&target_offset),
954 optional(&image_rect),
955 interpolation_mode,
956 composite_mode,
957 );
958 }
959 }
960
961 pub(crate) fn create_compatible_render_target(
964 &mut self,
965 width_f: f32,
966 height_f: f32,
967 ) -> Result<BitmapRenderTarget, Error> {
968 unsafe {
969 let mut ptr = null_mut();
970 let size_f = D2D1_SIZE_F {
971 width: width_f,
972 height: height_f,
973 };
974 let format = D2D1_PIXEL_FORMAT {
978 format: DXGI_FORMAT_R8G8B8A8_UNORM,
979 alphaMode: D2D1_ALPHA_MODE_PREMULTIPLIED,
980 };
981 let options = D2D1_COMPATIBLE_RENDER_TARGET_OPTIONS_NONE;
982 let hr =
983 self.0
984 .CreateCompatibleRenderTarget(&size_f, null(), &format, options, &mut ptr);
985 wrap(hr, ptr, BitmapRenderTarget)
986 }
987 }
988}
989
990impl PathGeometry {
991 pub fn open(&mut self) -> Result<GeometrySink<'_>, Error> {
992 unsafe {
993 let mut ptr = null_mut();
994 let hr = (self.0).Open(&mut ptr);
995 wrap(hr, ptr, |ptr| GeometrySink {
996 ptr,
997 marker: Default::default(),
998 })
999 }
1000 }
1001}
1002
1003impl<'a> GeometrySink<'a> {
1006 pub fn set_fill_mode(&mut self, fill_rule: FillRule) {
1007 let fill_mode = match fill_rule {
1008 FillRule::EvenOdd => D2D1_FILL_MODE_ALTERNATE,
1009 FillRule::NonZero => D2D1_FILL_MODE_WINDING,
1010 };
1011 unsafe {
1012 self.ptr.SetFillMode(fill_mode);
1013 }
1014 }
1015
1016 pub fn add_bezier(
1017 &mut self,
1018 point1: D2D1_POINT_2F,
1019 point2: D2D1_POINT_2F,
1020 point3: D2D1_POINT_2F,
1021 ) {
1022 let seg = D2D1_BEZIER_SEGMENT {
1023 point1,
1024 point2,
1025 point3,
1026 };
1027 unsafe {
1028 self.ptr.AddBezier(&seg);
1029 }
1030 }
1031
1032 pub fn add_quadratic_bezier(&mut self, point1: D2D1_POINT_2F, point2: D2D1_POINT_2F) {
1033 let seg = D2D1_QUADRATIC_BEZIER_SEGMENT { point1, point2 };
1034 unsafe {
1035 self.ptr.AddQuadraticBezier(&seg);
1036 }
1037 }
1038
1039 pub fn add_line(&mut self, point: D2D1_POINT_2F) {
1040 unsafe {
1041 self.ptr.AddLine(point);
1042 }
1043 }
1044
1045 pub fn begin_figure(&mut self, start: D2D1_POINT_2F, is_filled: bool) {
1046 unsafe {
1047 let figure_end = if is_filled {
1048 D2D1_FIGURE_BEGIN_FILLED
1049 } else {
1050 D2D1_FIGURE_BEGIN_HOLLOW
1051 };
1052 self.ptr.BeginFigure(start, figure_end);
1053 }
1054 }
1055
1056 pub fn end_figure(&mut self, is_closed: bool) {
1057 unsafe {
1058 let figure_end = if is_closed {
1059 D2D1_FIGURE_END_CLOSED
1060 } else {
1061 D2D1_FIGURE_END_OPEN
1062 };
1063 self.ptr.EndFigure(figure_end);
1064 }
1065 }
1066
1067 pub fn close(self) -> Result<(), Error> {
1069 unsafe { wrap_unit(self.ptr.Close()) }
1070 }
1071}
1072
1073impl Bitmap {
1075 pub fn get_size(&self) -> D2D1_SIZE_F {
1076 unsafe { self.inner.GetSize() }
1077 }
1078
1079 pub(crate) fn copy_from_render_target(
1080 &mut self,
1081 dest_point: D2D1_POINT_2U,
1082 rt: &mut DeviceContext,
1083 src_rect: D2D1_RECT_U,
1084 ) {
1085 unsafe {
1086 let rt = rt.get_raw() as *mut _;
1087 self.inner.CopyFromRenderTarget(&dest_point, rt, &src_rect);
1088 }
1089 }
1090}
1091
1092impl Effect {
1093 pub(crate) fn set_input(&self, index: u32, input: &ID2D1Image) {
1099 unsafe {
1100 self.0.SetInput(index, input, TRUE);
1101 }
1102 }
1103}
1104
1105impl BitmapRenderTarget {
1106 pub(crate) fn get_bitmap(&self) -> Result<ComPtr<ID2D1Bitmap>, Error> {
1110 unsafe {
1111 let mut ptr = null_mut();
1112 let hr = self.0.GetBitmap(&mut ptr);
1113 wrap(hr, ptr, |com_ptr| com_ptr)
1114 }
1115 }
1116}
1117
1118impl Deref for BitmapRenderTarget {
1121 type Target = ID2D1BitmapRenderTarget;
1122
1123 fn deref(&self) -> &Self::Target {
1124 &self.0
1125 }
1126}
1127
1128impl Brush {
1129 pub(crate) fn as_raw(&self) -> *mut ID2D1Brush {
1133 self.0.as_raw()
1134 }
1135}
1136
1137mod tests {
1138 use super::*;
1139
1140 #[test]
1141 fn geom_builder() {
1142 let mut factory = D2DFactory::new().unwrap();
1143 let mut p = factory.create_path_geometry().unwrap();
1144 let mut s1 = p.open().unwrap();
1145 s1.close();
1148 if let Ok(mut s2) = p.open() {
1149 s2.close();
1150 }
1151 }
1152}