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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
use brush::gradient::{GradientStop, GradientStopBuilder, GradientStopCollection};
use enums::*;
use error::D2DResult;
use math::{BrushProperties, LinearGradientBrushProperties, Matrix3x2F, Point2F};
use render_target::RenderTarget;
use std::mem;
use std::ptr;
use either::Either;
use winapi::shared::winerror::SUCCEEDED;
use winapi::um::d2d1::ID2D1LinearGradientBrush;
use wio::com::ComPtr;
#[derive(Clone)]
/// Paints an area with a linear gradient.
pub struct LinearGradientBrush {
ptr: ComPtr<ID2D1LinearGradientBrush>,
}
impl LinearGradientBrush {
#[inline]
pub fn create<'a, R>(context: &'a R) -> LinearGradientBrushBuilder<'a, R>
where
R: RenderTarget + 'a,
{
LinearGradientBrushBuilder::new(context)
}
/// Retrieves the starting coordinates of the linear gradient.
#[inline]
pub fn get_start_point(&self) -> Point2F {
unsafe { Point2F(self.ptr.GetStartPoint()) }
}
/// Retrieves the ending coordinates of the linear gradient.
#[inline]
pub fn get_end_point(&self) -> Point2F {
unsafe { Point2F(self.ptr.GetEndPoint()) }
}
/// Retrieves the `GradientStopCollection` associated with this linear gradient brush.
#[inline]
pub fn get_gradient_stop_collection(&self) -> GradientStopCollection {
unsafe {
let mut ptr = ptr::null_mut();
self.ptr.GetGradientStopCollection(&mut ptr);
GradientStopCollection {
ptr: ComPtr::from_raw(ptr),
}
}
}
/// Sets the starting coordinates of the linear gradient in the brush's coordinate space.
#[inline]
pub fn set_start_point(&mut self, point: Point2F) {
unsafe { self.ptr.SetStartPoint(point.0) }
}
/// Sets the ending coordinates of the linear gradient in the brush's coordinate space.
#[inline]
pub fn set_end_point(&mut self, point: Point2F) {
unsafe { self.ptr.SetEndPoint(point.0) }
}
}
brush_type!(LinearGradientBrush: ID2D1LinearGradientBrush);
pub struct LinearGradientBrushBuilder<'a, R>
where
R: RenderTarget + 'a,
{
context: &'a R,
properties: BrushProperties,
linear_properties: LinearGradientBrushProperties,
stops: Either<GradientStopBuilder<'a, R>, &'a GradientStopCollection>,
}
impl<'a, R> LinearGradientBrushBuilder<'a, R>
where
R: RenderTarget + 'a,
{
#[inline]
/// Creates a builder. It starts with the opacity as 1.0 and the transform as the identity
/// matrix, and all other values zeroed out. You should add at least two gradient stops.
pub fn new(context: &'a R) -> Self {
LinearGradientBrushBuilder {
context,
properties: BrushProperties::new(1.0, &Matrix3x2F::IDENTITY),
linear_properties: unsafe { mem::zeroed() },
stops: Either::Left(GradientStopBuilder::new(context)),
}
}
#[inline]
/// Build the brush described in this builder. Will likely fail if you haven't added any
/// stops to the collection (or specified an existing collection).
pub fn build(self) -> D2DResult<LinearGradientBrush> {
let stops = self.stops.map_left(|b| b.build());
let stops = stops.as_ref().either(|b| b.as_ref(), |&c| Ok(c))?;
unsafe {
let mut ptr = ptr::null_mut();
let hr = self.context.rt().CreateLinearGradientBrush(
&self.linear_properties.0,
&self.properties.0,
stops.get_raw(),
&mut ptr,
);
if SUCCEEDED(hr) {
Ok(LinearGradientBrush::from_raw(ptr))
} else {
Err(hr.into())
}
}
}
#[inline]
/// Sets the opacity and transform
pub fn with_properties(mut self, properties: BrushProperties) -> Self {
self.properties = properties;
self
}
#[inline]
/// This changes the overall opacity of the brush, which is essentially
/// multiplied with the colors in the gradient stops
pub fn with_opacity(mut self, opacity: f32) -> Self {
self.properties.0.opacity = opacity;
self
}
#[inline]
/// Sets a full affine transform on how the gradient is applied when rendered
pub fn with_transform(mut self, transform: Matrix3x2F) -> Self {
self.properties.0.transform = transform.0;
self
}
#[inline]
/// Sets the coordinate where the gradient stop at position 0.0 applies in full
pub fn with_start(mut self, start: Point2F) -> Self {
self.linear_properties.startPoint = start.0;
self
}
#[inline]
/// Sets the coordinate where the gradient stop at position 1.0 applies in full
pub fn with_end(mut self, end: Point2F) -> Self {
self.linear_properties.endPoint = end.0;
self
}
#[inline]
/// Sets how colors are determined for pixels below position 0.0 and above 1.0
/// in the gradient.
/// It's not valid to call this method if you are using `with_stop_collection`.
pub fn with_extend_mode(mut self, mode: ExtendMode) -> Self {
self.stops = Either::Left(
self.stops
.left()
.expect("Can't modify stops if collection was specified")
.with_extend_mode(mode),
);
self
}
#[inline]
/// Sets the gamma mode for the colors when creating a new gradient stop collection.
/// It's not valid to call this method if you are using `with_stop_collection`.
pub fn with_gamma(mut self, gamma: Gamma) -> Self {
self.stops = Either::Left(
self.stops
.left()
.expect("Can't modify stops if collection was specified")
.with_gamma(gamma),
);
self
}
#[inline]
/// Appends an individual GradientStop onto the list of stops which will be used
/// to create the collection.
/// It's not valid to call this method if you are using `with_stop_collection`.
pub fn with_stop<G>(mut self, stop: G) -> Self
where
G: Into<GradientStop>,
{
self.stops = Either::Left(
self.stops
.left()
.expect("Can't modify stops if collection was specified")
.with_stop(stop),
);
self
}
#[inline]
/// Appends a slice onto the list of stops which will be used to create the collection.
/// It is most efficient to call this method exactly once without using `with_stop` when
/// that is possible.
/// It's not valid to call this method if you are using `with_stop_collection`.
pub fn with_stops(mut self, stops: &'a [GradientStop]) -> Self {
self.stops = Either::Left(
self.stops
.left()
.expect("Can't modify stops if collection was specified")
.with_stops(stops),
);
self
}
#[inline]
/// Specifies that a pre-existing gradient stop collection should be used
pub fn with_stop_collection(mut self, stops: &'a GradientStopCollection) -> Self {
self.stops = Either::Right(stops);
self
}
}