Skip to main content

winio_ui_windows_common/
canvas.rs

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
554/// Drawing brush.
555pub 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
630/// Drawing pen.
631pub 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        // alpha premultiplication
687        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                    // do nothing
695                } 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}