1use std::{
2 cell::{Ref, RefCell},
3 mem::MaybeUninit,
4};
5
6use image::{DynamicImage, Pixel, Rgba, RgbaImage};
7use widestring::U16CString;
8use windows::{
9 Win32::Graphics::{
10 Direct2D::{
11 Common::{
12 D2D_RECT_F, D2D_SIZE_F, D2D_SIZE_U, D2D1_ALPHA_MODE_PREMULTIPLIED,
13 D2D1_BEZIER_SEGMENT, D2D1_COLOR_F, D2D1_FIGURE_BEGIN_HOLLOW,
14 D2D1_FIGURE_END_CLOSED, D2D1_FIGURE_END_OPEN, D2D1_GRADIENT_STOP,
15 D2D1_PIXEL_FORMAT,
16 },
17 D2D1_ARC_SEGMENT, D2D1_ARC_SIZE_LARGE, D2D1_ARC_SIZE_SMALL,
18 D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2D1_BITMAP_PROPERTIES,
19 D2D1_BRUSH_PROPERTIES, D2D1_DEFAULT_FLATTENING_TOLERANCE,
20 D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT, D2D1_ELLIPSE, D2D1_EXTEND_MODE_CLAMP,
21 D2D1_GAMMA_2_2, D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES,
22 D2D1_RADIAL_GRADIENT_BRUSH_PROPERTIES, D2D1_ROUNDED_RECT,
23 D2D1_SWEEP_DIRECTION_CLOCKWISE, D2D1_SWEEP_DIRECTION_COUNTER_CLOCKWISE, ID2D1Bitmap,
24 ID2D1Brush, ID2D1Factory, ID2D1Geometry, ID2D1GeometrySink, ID2D1PathGeometry,
25 ID2D1RenderTarget,
26 },
27 DirectWrite::{
28 DWRITE_FONT_STRETCH_NORMAL, DWRITE_FONT_STYLE_ITALIC, DWRITE_FONT_STYLE_NORMAL,
29 DWRITE_FONT_WEIGHT_BOLD, DWRITE_FONT_WEIGHT_NORMAL, IDWriteFactory, IDWriteTextLayout,
30 },
31 Dxgi::Common::DXGI_FORMAT_R8G8B8A8_UNORM,
32 },
33 core::Interface,
34};
35use windows_numerics::{Matrix3x2, Vector2};
36use winio_primitive::{
37 BrushPen, Color, DrawingFont, GradientStop, HAlign, LinearGradientBrush, Point,
38 RadialGradientBrush, Rect, RectBox, RelativeToLogical, Size, SolidColorBrush, Transform,
39 VAlign, Vector,
40};
41
42use crate::Result;
43
44fn color_f(c: Color) -> D2D1_COLOR_F {
45 D2D1_COLOR_F {
46 r: c.r as f32 / 255.0,
47 g: c.g as f32 / 255.0,
48 b: c.b as f32 / 255.0,
49 a: c.a as f32 / 255.0,
50 }
51}
52
53const fn point_2f(p: Point) -> Vector2 {
54 Vector2 {
55 X: p.x as f32,
56 Y: p.y as f32,
57 }
58}
59
60const fn size_f(s: Size) -> D2D_SIZE_F {
61 D2D_SIZE_F {
62 width: s.width as f32,
63 height: s.height as f32,
64 }
65}
66
67fn rect_f(r: Rect) -> D2D_RECT_F {
68 D2D_RECT_F {
69 left: r.origin.x as f32,
70 top: r.origin.y as f32,
71 right: (r.origin.x + r.size.width) as f32,
72 bottom: (r.origin.y + r.size.height) as f32,
73 }
74}
75
76fn matrix_f(m: Transform) -> Matrix3x2 {
77 Matrix3x2 {
78 M11: m.m11 as _,
79 M12: m.m12 as _,
80 M21: m.m21 as _,
81 M22: m.m22 as _,
82 M31: m.m31 as _,
83 M32: m.m32 as _,
84 }
85}
86
87fn gradient_stop(s: &GradientStop) -> D2D1_GRADIENT_STOP {
88 D2D1_GRADIENT_STOP {
89 position: s.pos as f32,
90 color: color_f(s.color),
91 }
92}
93
94pub struct DrawingContext {
95 d2d: ID2D1Factory,
96 dwrite: IDWriteFactory,
97 target: ID2D1RenderTarget,
98}
99
100#[inline]
101fn to_trans(rect: Rect) -> RelativeToLogical {
102 RelativeToLogical::scale(rect.size.width, rect.size.height)
103 .then_translate(rect.origin.to_vector())
104}
105
106fn get_arc(rect: Rect, start: f64, end: f64) -> (Size, Point, Point, Point) {
107 let radius = rect.size / 2.0;
108 let centerp = rect.origin.add_size(&radius);
109 let startp = centerp + Vector::new(radius.width * start.cos(), radius.height * start.sin());
110 let endp = centerp + Vector::new(radius.width * end.cos(), radius.height * end.sin());
111 (radius, centerp, startp, endp)
112}
113
114fn ellipse(rect: Rect) -> D2D1_ELLIPSE {
115 D2D1_ELLIPSE {
116 point: point_2f(rect.origin.add_size(&(rect.size / 2.0))),
117 radiusX: (rect.size.width / 2.0) as f32,
118 radiusY: (rect.size.height / 2.0) as f32,
119 }
120}
121
122impl DrawingContext {
123 pub fn new(d2d: ID2D1Factory, dwrite: IDWriteFactory, target: ID2D1RenderTarget) -> Self {
124 Self {
125 d2d,
126 dwrite,
127 target,
128 }
129 }
130
131 pub fn render_target(&self) -> &ID2D1RenderTarget {
132 &self.target
133 }
134
135 #[inline]
136 fn get_brush(&self, brush: impl Brush, rect: Rect) -> Result<ID2D1Brush> {
137 brush.create(&self.target, to_trans(rect))
138 }
139
140 #[inline]
141 fn get_pen(&self, pen: impl Pen, rect: Rect) -> Result<(ID2D1Brush, f32)> {
142 pen.create(&self.target, to_trans(rect))
143 }
144
145 fn get_arc_geo(&self, rect: Rect, start: f64, end: f64, close: bool) -> Result<ID2D1Geometry> {
146 unsafe {
147 let geo = self.d2d.CreatePathGeometry()?;
148 let sink = geo.Open()?;
149 let (radius, centerp, startp, endp) = get_arc(rect, start, end);
150 sink.BeginFigure(point_2f(startp), D2D1_FIGURE_BEGIN_HOLLOW);
151 sink.AddArc(&D2D1_ARC_SEGMENT {
152 point: point_2f(endp),
153 size: size_f(radius),
154 rotationAngle: 0.0,
155 sweepDirection: D2D1_SWEEP_DIRECTION_CLOCKWISE,
156 arcSize: if (end - start) > std::f64::consts::PI {
157 D2D1_ARC_SIZE_LARGE
158 } else {
159 D2D1_ARC_SIZE_SMALL
160 },
161 });
162 if close {
163 sink.AddLine(point_2f(centerp));
164 }
165 sink.EndFigure(if close {
166 D2D1_FIGURE_END_CLOSED
167 } else {
168 D2D1_FIGURE_END_OPEN
169 });
170 sink.Close()?;
171 geo.cast()
172 }
173 }
174
175 fn get_str_layout(
176 &self,
177 font: DrawingFont,
178 mut pos: Point,
179 s: &str,
180 ) -> Result<(Rect, IDWriteTextLayout)> {
181 unsafe {
182 let f = U16CString::from_str_truncate(&font.family);
183 let format = self.dwrite.CreateTextFormat(
184 windows::core::PCWSTR::from_raw(f.as_ptr()),
185 None,
186 if font.bold {
187 DWRITE_FONT_WEIGHT_BOLD
188 } else {
189 DWRITE_FONT_WEIGHT_NORMAL
190 },
191 if font.italic {
192 DWRITE_FONT_STYLE_ITALIC
193 } else {
194 DWRITE_FONT_STYLE_NORMAL
195 },
196 DWRITE_FONT_STRETCH_NORMAL,
197 font.size as f32,
198 windows::core::w!(""),
199 )?;
200 let size = self.target.GetSize();
201 let s = U16CString::from_str_truncate(s);
202 let layout =
203 self.dwrite
204 .CreateTextLayout(s.as_slice(), &format, size.width, size.height)?;
205 let mut metrics = MaybeUninit::uninit();
206 layout.GetMetrics(metrics.as_mut_ptr())?;
207 let metrics = metrics.assume_init();
208 match font.halign {
209 HAlign::Center => {
210 pos.x -= metrics.width as f64 / 2.0;
211 }
212 HAlign::Right => {
213 pos.x -= metrics.width as f64;
214 }
215 _ => {}
216 }
217 match font.valign {
218 VAlign::Center => {
219 pos.y -= metrics.height as f64 / 2.0;
220 }
221 VAlign::Bottom => {
222 pos.y -= metrics.height as f64;
223 }
224 _ => {}
225 }
226 let size = Size::new(metrics.width as f64, metrics.height as f64);
227 let rect = Rect::new(pos, size);
228 Ok((rect, layout))
229 }
230 }
231
232 pub fn set_transform(&mut self, transform: Transform) -> Result<()> {
233 unsafe {
234 let matrix = matrix_f(transform);
235 self.target.SetTransform(&matrix);
236 }
237 Ok(())
238 }
239
240 pub fn transform(&self) -> Result<Transform> {
241 let mut matrix = MaybeUninit::uninit();
242 let matrix = unsafe {
243 self.target.GetTransform(matrix.as_mut_ptr());
244 matrix.assume_init()
245 };
246 Ok(Transform::new(
247 matrix.M11 as f64,
248 matrix.M12 as f64,
249 matrix.M21 as f64,
250 matrix.M22 as f64,
251 matrix.M31 as f64,
252 matrix.M32 as f64,
253 ))
254 }
255
256 pub fn draw_path(&mut self, pen: impl Pen, path: &DrawingPath) -> Result<()> {
257 let width = pen.width();
258 let rect = unsafe {
259 path.geo
260 .GetWidenedBounds(width, None, None, D2D1_DEFAULT_FLATTENING_TOLERANCE)?
261 };
262 let (b, width) = self.get_pen(
263 pen,
264 RectBox::new(
265 Point::new(rect.left as _, rect.top as _),
266 Point::new(rect.right as _, rect.bottom as _),
267 )
268 .to_rect(),
269 )?;
270 unsafe {
271 self.target.DrawGeometry(&path.geo, &b, width, None);
272 }
273 Ok(())
274 }
275
276 pub fn fill_path(&mut self, brush: impl Brush, path: &DrawingPath) -> Result<()> {
277 let rect = unsafe { path.geo.GetBounds(None)? };
278 let b = self.get_brush(
279 brush,
280 RectBox::new(
281 Point::new(rect.left as _, rect.top as _),
282 Point::new(rect.right as _, rect.bottom as _),
283 )
284 .to_rect(),
285 )?;
286 unsafe {
287 self.target.FillGeometry(&path.geo, &b, None);
288 }
289 Ok(())
290 }
291
292 pub fn draw_arc(&mut self, pen: impl Pen, rect: Rect, start: f64, end: f64) -> Result<()> {
293 let geo = self.get_arc_geo(rect, start, end, false)?;
294 let (b, width) = self.get_pen(pen, rect)?;
295 unsafe {
296 self.target.DrawGeometry(&geo, &b, width, None);
297 }
298 Ok(())
299 }
300
301 pub fn draw_pie(&mut self, pen: impl Pen, rect: Rect, start: f64, end: f64) -> Result<()> {
302 let geo = self.get_arc_geo(rect, start, end, true)?;
303 let (b, width) = self.get_pen(pen, rect)?;
304 unsafe {
305 self.target.DrawGeometry(&geo, &b, width, None);
306 }
307 Ok(())
308 }
309
310 pub fn fill_pie(&mut self, brush: impl Brush, rect: Rect, start: f64, end: f64) -> Result<()> {
311 let geo = self.get_arc_geo(rect, start, end, true)?;
312 let b = self.get_brush(brush, rect)?;
313 unsafe {
314 self.target.FillGeometry(&geo, &b, None);
315 }
316 Ok(())
317 }
318
319 pub fn draw_ellipse(&mut self, pen: impl Pen, rect: Rect) -> Result<()> {
320 let e = ellipse(rect);
321 let (b, width) = self.get_pen(pen, rect)?;
322 unsafe {
323 self.target.DrawEllipse(&e, &b, width, None);
324 }
325 Ok(())
326 }
327
328 pub fn fill_ellipse(&mut self, brush: impl Brush, rect: Rect) -> Result<()> {
329 let e = ellipse(rect);
330 let b = self.get_brush(brush, rect)?;
331 unsafe {
332 self.target.FillEllipse(&e, &b);
333 }
334 Ok(())
335 }
336
337 pub fn draw_line(&mut self, pen: impl Pen, start: Point, end: Point) -> Result<()> {
338 let rect = RectBox::new(
339 Point::new(start.x.min(end.x), start.y.min(end.y)),
340 Point::new(start.x.max(end.x), start.y.max(end.y)),
341 )
342 .to_rect();
343 let (b, width) = self.get_pen(pen, rect)?;
344 unsafe {
345 self.target
346 .DrawLine(point_2f(start), point_2f(end), &b, width, None);
347 }
348 Ok(())
349 }
350
351 pub fn draw_rect(&mut self, pen: impl Pen, rect: Rect) -> Result<()> {
352 let (b, width) = self.get_pen(pen, rect)?;
353 unsafe {
354 self.target.DrawRectangle(&rect_f(rect), &b, width, None);
355 }
356 Ok(())
357 }
358
359 pub fn fill_rect(&mut self, brush: impl Brush, rect: Rect) -> Result<()> {
360 let b = self.get_brush(brush, rect)?;
361 unsafe {
362 self.target.FillRectangle(&rect_f(rect), &b);
363 }
364 Ok(())
365 }
366
367 pub fn draw_round_rect(&mut self, pen: impl Pen, rect: Rect, round: Size) -> Result<()> {
368 let (b, width) = self.get_pen(pen, rect)?;
369 unsafe {
370 self.target.DrawRoundedRectangle(
371 &D2D1_ROUNDED_RECT {
372 rect: rect_f(rect),
373 radiusX: round.width as f32,
374 radiusY: round.height as f32,
375 },
376 &b,
377 width,
378 None,
379 );
380 }
381 Ok(())
382 }
383
384 pub fn fill_round_rect(&mut self, brush: impl Brush, rect: Rect, round: Size) -> Result<()> {
385 let b = self.get_brush(brush, rect)?;
386 unsafe {
387 self.target.FillRoundedRectangle(
388 &D2D1_ROUNDED_RECT {
389 rect: rect_f(rect),
390 radiusX: round.width as f32,
391 radiusY: round.height as f32,
392 },
393 &b,
394 );
395 }
396 Ok(())
397 }
398
399 pub fn draw_str(
400 &mut self,
401 brush: impl Brush,
402 font: DrawingFont,
403 pos: Point,
404 text: &str,
405 ) -> Result<()> {
406 let (rect, layout) = self.get_str_layout(font, pos, text.as_ref())?;
407 let b = self.get_brush(brush, rect)?;
408 unsafe {
409 self.target.DrawTextLayout(
410 point_2f(rect.origin),
411 &layout,
412 &b,
413 D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT,
414 );
415 }
416 Ok(())
417 }
418
419 pub fn measure_str(&self, font: DrawingFont, text: &str) -> Result<Size> {
420 let (rect, _) = self.get_str_layout(font, Point::zero(), text.as_ref())?;
421 Ok(rect.size)
422 }
423
424 pub fn create_image(&self, image: DynamicImage) -> Result<DrawingImage> {
425 DrawingImage::new(&self.target, image)
426 }
427
428 pub fn draw_image(
429 &mut self,
430 image: &DrawingImage,
431 rect: Rect,
432 clip: Option<Rect>,
433 ) -> Result<()> {
434 unsafe {
435 let clip = clip.map(rect_f);
436 self.target.DrawBitmap(
437 &*image.get_bitmap(&self.target)?,
438 Some(&rect_f(rect)),
439 1.0,
440 D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR,
441 clip.as_ref().map(|r| r as *const _),
442 );
443 }
444 Ok(())
445 }
446
447 pub fn create_path_builder(&self, start: Point) -> Result<DrawingPathBuilder> {
448 DrawingPathBuilder::new(&self.d2d, start)
449 }
450}
451
452pub struct DrawingPath {
453 geo: ID2D1Geometry,
454}
455
456impl DrawingPath {
457 fn new(geo: ID2D1Geometry) -> Self {
458 Self { geo }
459 }
460}
461
462pub struct DrawingPathBuilder {
463 geo: ID2D1PathGeometry,
464 sink: ID2D1GeometrySink,
465}
466
467impl DrawingPathBuilder {
468 fn new(d2d: &ID2D1Factory, start: Point) -> Result<Self> {
469 unsafe {
470 let geo = d2d.CreatePathGeometry()?;
471 let sink = geo.Open()?;
472 sink.BeginFigure(point_2f(start), D2D1_FIGURE_BEGIN_HOLLOW);
473 Ok(Self { geo, sink })
474 }
475 }
476
477 pub fn add_line(&mut self, p: Point) -> Result<()> {
478 unsafe {
479 self.sink.AddLine(point_2f(p));
480 }
481 Ok(())
482 }
483
484 pub fn add_arc(
485 &mut self,
486 center: Point,
487 radius: Size,
488 start: f64,
489 end: f64,
490 clockwise: bool,
491 ) -> Result<()> {
492 unsafe {
493 let startp =
494 center + Vector::new(radius.width * start.cos(), radius.height * start.sin());
495 let endp = center + Vector::new(radius.width * end.cos(), radius.height * end.sin());
496 self.add_line(startp)?;
497 self.sink.AddArc(&D2D1_ARC_SEGMENT {
498 point: point_2f(endp),
499 size: size_f(radius),
500 rotationAngle: 0.0,
501 sweepDirection: if clockwise {
502 D2D1_SWEEP_DIRECTION_CLOCKWISE
503 } else {
504 D2D1_SWEEP_DIRECTION_COUNTER_CLOCKWISE
505 },
506 arcSize: if (end - start) > std::f64::consts::PI {
507 D2D1_ARC_SIZE_LARGE
508 } else {
509 D2D1_ARC_SIZE_SMALL
510 },
511 });
512 }
513 Ok(())
514 }
515
516 pub fn add_bezier(&mut self, p1: Point, p2: Point, p3: Point) -> Result<()> {
517 unsafe {
518 self.sink.AddBezier(&D2D1_BEZIER_SEGMENT {
519 point1: point_2f(p1),
520 point2: point_2f(p2),
521 point3: point_2f(p3),
522 });
523 }
524 Ok(())
525 }
526
527 pub fn build(self, close: bool) -> Result<DrawingPath> {
528 unsafe {
529 self.sink.EndFigure(if close {
530 D2D1_FIGURE_END_CLOSED
531 } else {
532 D2D1_FIGURE_END_OPEN
533 });
534 self.sink.Close()?;
535 Ok(DrawingPath::new(self.geo.cast()?))
536 }
537 }
538}
539
540const MATRIX_IDENTITY: Matrix3x2 = Matrix3x2 {
541 M11: 1.0,
542 M12: 0.0,
543 M21: 0.0,
544 M22: 1.0,
545 M31: 0.0,
546 M32: 0.0,
547};
548
549const BRUSH_PROPERTIES_DEFAULT: D2D1_BRUSH_PROPERTIES = D2D1_BRUSH_PROPERTIES {
550 opacity: 1.0,
551 transform: MATRIX_IDENTITY,
552};
553
554pub trait Brush {
556 #[doc(hidden)]
557 fn create(&self, target: &ID2D1RenderTarget, trans: RelativeToLogical) -> Result<ID2D1Brush>;
558}
559
560impl<B: Brush> Brush for &'_ B {
561 fn create(&self, target: &ID2D1RenderTarget, trans: RelativeToLogical) -> Result<ID2D1Brush> {
562 (**self).create(target, trans)
563 }
564}
565
566impl Brush for SolidColorBrush {
567 fn create(&self, target: &ID2D1RenderTarget, _trans: RelativeToLogical) -> Result<ID2D1Brush> {
568 unsafe {
569 target
570 .CreateSolidColorBrush(&color_f(self.color), Some(&BRUSH_PROPERTIES_DEFAULT))?
571 .cast()
572 }
573 }
574}
575
576impl Brush for LinearGradientBrush {
577 fn create(&self, target: &ID2D1RenderTarget, trans: RelativeToLogical) -> Result<ID2D1Brush> {
578 let props = D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES {
579 startPoint: point_2f(trans.transform_point(self.start)),
580 endPoint: point_2f(trans.transform_point(self.end)),
581 };
582 let stops = self.stops.iter().map(gradient_stop).collect::<Vec<_>>();
583 unsafe {
584 let stop_collection = target.CreateGradientStopCollection(
585 &stops,
586 D2D1_GAMMA_2_2,
587 D2D1_EXTEND_MODE_CLAMP,
588 )?;
589 target
590 .CreateLinearGradientBrush(
591 &props,
592 Some(&BRUSH_PROPERTIES_DEFAULT),
593 &stop_collection,
594 )?
595 .cast()
596 }
597 }
598}
599
600impl Brush for RadialGradientBrush {
601 fn create(&self, target: &ID2D1RenderTarget, trans: RelativeToLogical) -> Result<ID2D1Brush> {
602 let radius = self.radius.to_vector();
603 let radius = trans.transform_vector(radius);
604 let props = D2D1_RADIAL_GRADIENT_BRUSH_PROPERTIES {
605 center: point_2f(trans.transform_point(self.center)),
606 gradientOriginOffset: point_2f(
607 trans.transform_vector(self.origin - self.center).to_point(),
608 ),
609 radiusX: radius.x as f32,
610 radiusY: radius.y as f32,
611 };
612 let stops = self.stops.iter().map(gradient_stop).collect::<Vec<_>>();
613 unsafe {
614 let stop_collection = target.CreateGradientStopCollection(
615 &stops,
616 D2D1_GAMMA_2_2,
617 D2D1_EXTEND_MODE_CLAMP,
618 )?;
619 target
620 .CreateRadialGradientBrush(
621 &props,
622 Some(&BRUSH_PROPERTIES_DEFAULT),
623 &stop_collection,
624 )?
625 .cast()
626 }
627 }
628}
629
630pub trait Pen {
632 #[doc(hidden)]
633 fn create(
634 &self,
635 target: &ID2D1RenderTarget,
636 trans: RelativeToLogical,
637 ) -> Result<(ID2D1Brush, f32)>;
638 #[doc(hidden)]
639 fn width(&self) -> f32;
640}
641
642impl<P: Pen> Pen for &'_ P {
643 fn create(
644 &self,
645 target: &ID2D1RenderTarget,
646 trans: RelativeToLogical,
647 ) -> Result<(ID2D1Brush, f32)> {
648 (**self).create(target, trans)
649 }
650
651 fn width(&self) -> f32 {
652 (**self).width()
653 }
654}
655
656impl<B: Brush> Pen for BrushPen<B> {
657 fn create(
658 &self,
659 target: &ID2D1RenderTarget,
660 trans: RelativeToLogical,
661 ) -> Result<(ID2D1Brush, f32)> {
662 let brush = self.brush.create(target, trans)?;
663 Ok((brush, self.width as _))
664 }
665
666 fn width(&self) -> f32 {
667 self.width as _
668 }
669}
670
671pub struct DrawingImage {
672 image: RgbaImage,
673 target: RefCell<ID2D1RenderTarget>,
674 bitmap: RefCell<ID2D1Bitmap>,
675}
676
677impl DrawingImage {
678 fn new(target: &ID2D1RenderTarget, image: DynamicImage) -> Result<Self> {
679 let (mut image, has_alpha) = match image {
680 DynamicImage::ImageRgb8(_)
681 | DynamicImage::ImageRgb16(_)
682 | DynamicImage::ImageRgb32F(_) => (image.into_rgba8(), false),
683 DynamicImage::ImageRgba8(image) => (image, true),
684 _ => (image.into_rgba8(), true),
685 };
686 if has_alpha {
688 for Rgba(pixel) in image.pixels_mut() {
689 if pixel[3] == 0 {
690 pixel[0] = 0;
691 pixel[1] = 0;
692 pixel[2] = 0;
693 } else if pixel[3] == 255 {
694 } else {
696 let a = pixel[3] as f32 / 255.0;
697 pixel[0] = ((pixel[0] as f32) * a).round() as u8;
698 pixel[1] = ((pixel[1] as f32) * a).round() as u8;
699 pixel[2] = ((pixel[2] as f32) * a).round() as u8;
700 }
701 }
702 }
703 let bitmap = Self::create_bitmap(target, &image)?;
704 Ok(Self {
705 image,
706 target: RefCell::new(target.clone()),
707 bitmap: RefCell::new(bitmap),
708 })
709 }
710
711 fn create_bitmap(target: &ID2D1RenderTarget, image: &RgbaImage) -> Result<ID2D1Bitmap> {
712 let mut dpix = 0.0;
713 let mut dpiy = 0.0;
714 unsafe { target.GetDpi(&mut dpix, &mut dpiy) };
715 let prop = D2D1_BITMAP_PROPERTIES {
716 pixelFormat: D2D1_PIXEL_FORMAT {
717 format: DXGI_FORMAT_R8G8B8A8_UNORM,
718 alphaMode: D2D1_ALPHA_MODE_PREMULTIPLIED,
719 },
720 dpiX: dpix,
721 dpiY: dpiy,
722 };
723 unsafe {
724 target.CreateBitmap(
725 D2D_SIZE_U {
726 width: image.width(),
727 height: image.height(),
728 },
729 Some(image.as_ptr().cast()),
730 image.width() * Rgba::<u8>::CHANNEL_COUNT as u32,
731 &prop,
732 )
733 }
734 }
735
736 fn recreate(&self, target: &ID2D1RenderTarget) -> Result<()> {
737 *self.bitmap.borrow_mut() = Self::create_bitmap(target, &self.image)?;
738 *self.target.borrow_mut() = target.clone();
739 Ok(())
740 }
741
742 pub fn get_bitmap(&self, target: &ID2D1RenderTarget) -> Result<Ref<'_, ID2D1Bitmap>> {
743 if self.target.borrow().as_raw() != target.as_raw() {
744 self.recreate(target)?;
745 }
746 Ok(self.bitmap.borrow())
747 }
748
749 pub fn size(&self) -> Result<Size> {
750 let size = unsafe { self.bitmap.borrow().GetSize() };
751 Ok(Size::new(size.width as _, size.height as _))
752 }
753}