1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
use crate::numerics::*;
use bevy_ecs::prelude::*;
use core::hash::*;
use windows::Win32::Graphics::Direct2D::Common::*;
use windows::Win32::Graphics::Direct2D::*;
use windows::core::Result;
use windows_core::*;
#[derive(Component, Clone, Debug, PartialEq)]
pub struct VisualDrawContent {
pub local_aabb: Aabb,
pub command_list: ID2D1CommandList,
pub interpolation_mode: D2D1_INTERPOLATION_MODE,
pub composite_mode: D2D1_COMPOSITE_MODE,
}
unsafe impl Send for VisualDrawContent {}
unsafe impl Sync for VisualDrawContent {}
impl Hash for VisualDrawContent {
fn hash<H: Hasher>(&self, state: &mut H) {
self.local_aabb.hash(state);
(self.command_list.as_raw() as usize).hash(state);
self.interpolation_mode.0.hash(state);
self.composite_mode.0.hash(state);
}
}
impl VisualDrawContent {
/// 事前に計算済みの `local_aabb` を用いて `VisualDrawContent` を構築する。
///
/// # 引数
/// - `local_aabb`: コンテンツがローカル座標で占める AABB。
/// **論理単位(96DPI)** で与えること。DPI に依存した値を
/// 入れると、モニタ跨ぎや DPI 変更時に内在プロパティとしての不変性が
/// 崩れるため注意。
/// - `command_list`: このコンテンツの描画コマンド列。呼び出し側で
/// [`ID2D1CommandList::Close`] 済みであることを想定する。
/// - `interpolation_mode`: 描画時の補間モード。
/// - `composite_mode`: 描画時の合成モード。
///
/// バウンズが既知の場合はこちらを使う。未知で DC から求めたい場合は
/// [`Self::new_calc_aabb`] を使用する。
#[inline]
pub fn new_ex(
local_aabb: Aabb,
command_list: ID2D1CommandList,
interpolation_mode: D2D1_INTERPOLATION_MODE,
composite_mode: D2D1_COMPOSITE_MODE,
) -> Self {
Self {
local_aabb,
command_list,
interpolation_mode,
composite_mode,
}
}
/// `command_list` のローカル AABB を DC から算出して `VisualDrawContent` を構築する。
///
/// [`ID2D1DeviceContext::GetImageLocalBounds`] は world transform を含まない
/// ローカル空間のバウンズを返すが、その値は **DC の現在の DPI・UnitMode・
/// InterpolationMode を反映する**。したがって `local_aabb` を論理単位
/// (96DPI)として保持したい場合、渡す `dc` は
/// **DPI=96 / `D2D1_UNIT_MODE_DIPS`** に設定されていなければならない。
/// 画面 DPI や UnitMode=Pixels の DC を渡すと、得られる AABB がその DPI に
/// スケールされてしまい、モニタ跨ぎで再計算が必要になる。
///
/// 実運用では描画本番 DC とは別に、DPI=96/DIPs へ固定した
/// 「バウンズ計算専用 DC」を渡すことを推奨する。
///
/// # 引数
/// - `command_list`: 対象の描画コマンド列。**[`ID2D1CommandList::Close`]
/// 済みであること**。未クローズだと正しいバウンズが得られない。
/// - `interpolation_mode`: 描画時の補間モード。
/// - `composite_mode`: 描画時の合成モード。
/// - `dc`: バウンズ算出に用いるデバイスコンテキスト。上記のとおり
/// DPI=96 / UnitMode=DIPs を満たすこと。`command_list` と同一
/// `ID2D1Device` から生成された DC である必要がある。
///
/// # 戻り値
/// 算出した論理単位の `local_aabb` を持つ `VisualDrawContent`。
/// `GetImageLocalBounds` が失敗した場合はその [`windows::core::Error`] を返す。
///
/// # Safety / 実装メモ
/// `&ID2D1CommandList` から `Param<ID2D1Image>` への変換は `QUERY = false`
/// (ポインタ再解釈のみ)で解決され、`QueryInterface` は発生しない。
/// `unsafe` は COM 呼び出しのためで、`dc` が有効な参照であり `command_list`
/// が Close 済みであることが前提。
#[inline]
pub fn new_calc_aabb_ex(
command_list: ID2D1CommandList,
interpolation_mode: D2D1_INTERPOLATION_MODE,
composite_mode: D2D1_COMPOSITE_MODE,
dc: &ID2D1DeviceContext,
) -> Result<Self> {
let local_aabb = unsafe { dc.GetImageLocalBounds(&command_list) }?.into();
Ok(Self::new_ex(
local_aabb,
command_list,
interpolation_mode,
composite_mode,
))
}
#[inline]
pub fn new(local_aabb: Aabb, command_list: ID2D1CommandList) -> Self {
Self::new_ex(
local_aabb,
command_list,
D2D1_INTERPOLATION_MODE_LINEAR,
D2D1_COMPOSITE_MODE_SOURCE_OVER,
)
}
#[inline]
pub fn new_calc_aabb(command_list: ID2D1CommandList, dc: &ID2D1DeviceContext) -> Result<Self> {
Self::new_calc_aabb_ex(
command_list,
D2D1_INTERPOLATION_MODE_LINEAR,
D2D1_COMPOSITE_MODE_SOURCE_OVER,
dc,
)
}
}