1 | // TODO: prefix paint creation functions with make_ or new_ |
2 | // so that they are easier to find when autocompleting |
3 | |
4 | use std::rc::Rc; |
5 | |
6 | use crate::{geometry::Position, Align, Baseline, Color, FillRule, FontId, ImageId, LineCap, LineJoin}; |
7 | |
8 | #[derive (Copy, Clone, Debug, PartialEq, Default)] |
9 | #[cfg_attr (feature = "serde" , derive(Serialize, Deserialize))] |
10 | pub struct GradientStop(pub f32, pub Color); |
11 | |
12 | // We use MultiStopGradient as a key since we cache them. We either need |
13 | // to define Hash (for HashMap) or Ord for (BTreeMap). |
14 | impl Eq for GradientStop {} |
15 | impl Ord for GradientStop { |
16 | fn cmp(&self, other: &Self) -> std::cmp::Ordering { |
17 | if (other.0, other.1) < (self.0, self.1) { |
18 | std::cmp::Ordering::Less |
19 | } else if (self.0, self.1) < (other.0, other.1) { |
20 | std::cmp::Ordering::Greater |
21 | } else { |
22 | std::cmp::Ordering::Equal |
23 | } |
24 | } |
25 | } |
26 | |
27 | impl PartialOrd for GradientStop { |
28 | fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { |
29 | Some(self.cmp(other)) |
30 | } |
31 | } |
32 | |
33 | #[derive (Clone, Debug, PartialEq)] |
34 | #[cfg_attr (feature = "serde" , derive(Serialize, Deserialize))] |
35 | pub struct MultiStopGradient { |
36 | shared_stops: Rc<[GradientStop]>, |
37 | tint: f32, |
38 | } |
39 | |
40 | impl MultiStopGradient { |
41 | pub(crate) fn get(&self, index: usize) -> GradientStop { |
42 | let mut stop: GradientStop = self |
43 | .shared_stops |
44 | .get(index) |
45 | .copied() |
46 | .unwrap_or_else(|| GradientStop(2.0, Color::black())); |
47 | |
48 | stop.1.a *= self.tint; |
49 | stop |
50 | } |
51 | |
52 | pub(crate) fn pairs(&self) -> impl Iterator<Item = [GradientStop; 2]> + '_ { |
53 | self.shared_stops.as_ref().windows(size:2).map(move |pair: &[GradientStop]| { |
54 | let mut stops: [GradientStop; 2] = [pair[0], pair[1]]; |
55 | stops[0].1.a *= self.tint; |
56 | stops[1].1.a *= self.tint; |
57 | stops |
58 | }) |
59 | } |
60 | } |
61 | |
62 | impl Eq for MultiStopGradient {} |
63 | |
64 | impl PartialOrd for MultiStopGradient { |
65 | fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { |
66 | Some(self.cmp(other)) |
67 | } |
68 | } |
69 | |
70 | impl Ord for MultiStopGradient { |
71 | fn cmp(&self, other: &Self) -> std::cmp::Ordering { |
72 | if (&other.shared_stops, other.tint) < (&self.shared_stops, self.tint) { |
73 | std::cmp::Ordering::Less |
74 | } else if (&self.shared_stops, self.tint) < (&other.shared_stops, other.tint) { |
75 | std::cmp::Ordering::Greater |
76 | } else { |
77 | std::cmp::Ordering::Equal |
78 | } |
79 | } |
80 | } |
81 | |
82 | #[allow (clippy::large_enum_variant)] |
83 | #[derive (Clone, Debug, PartialEq, PartialOrd)] |
84 | #[cfg_attr (feature = "serde" , derive(Serialize, Deserialize))] |
85 | pub enum GradientColors { |
86 | TwoStop { start_color: Color, end_color: Color }, |
87 | MultiStop { stops: MultiStopGradient }, |
88 | } |
89 | impl GradientColors { |
90 | fn mul_alpha(&mut self, a: f32) { |
91 | match self { |
92 | Self::TwoStop { start_color, end_color } => { |
93 | start_color.a *= a; |
94 | end_color.a *= a; |
95 | } |
96 | Self::MultiStop { stops, .. } => { |
97 | stops.tint *= a; |
98 | } |
99 | } |
100 | } |
101 | fn from_stops<Stops>(stops: Stops) -> Self |
102 | where |
103 | Stops: IntoIterator<Item = (f32, Color)>, |
104 | { |
105 | let mut stops = stops.into_iter(); |
106 | let Some(first_stop) = stops.next() else { |
107 | // No stops, we use black. |
108 | return Self::TwoStop { |
109 | start_color: Color::black(), |
110 | end_color: Color::black(), |
111 | }; |
112 | }; |
113 | let Some(second_stop) = stops.next() else { |
114 | // One stop devolves to a solid color fill (but using the gradient shader variation). |
115 | return Self::TwoStop { |
116 | start_color: first_stop.1, |
117 | end_color: first_stop.1, |
118 | }; |
119 | }; |
120 | |
121 | let maybe_third_stop = stops.next(); |
122 | |
123 | if maybe_third_stop.is_none() && first_stop.0 <= 0.0 && second_stop.0 >= 1.0 { |
124 | // Two stops takes the classic gradient path, so long as the stop positions are at |
125 | // the extents (if the stop positions are inset then we'll fill to them). |
126 | return Self::TwoStop { |
127 | start_color: first_stop.1, |
128 | end_color: second_stop.1, |
129 | }; |
130 | } |
131 | |
132 | // Actual multistop gradient. We copy out the stops and then use a stop with a |
133 | // position > 1.0 as a sentinel. GradientStore ignores stop positions > 1.0 |
134 | // when synthesizing the gradient texture. |
135 | let out_stops = [first_stop, second_stop] |
136 | .into_iter() |
137 | .chain(maybe_third_stop) |
138 | .chain(stops) |
139 | .map(|(stop, color)| GradientStop(stop, color)) |
140 | .collect(); |
141 | Self::MultiStop { |
142 | stops: MultiStopGradient { |
143 | shared_stops: out_stops, |
144 | tint: 1.0, |
145 | }, |
146 | } |
147 | } |
148 | } |
149 | |
150 | #[derive (Clone, Debug)] |
151 | #[cfg_attr (feature = "serde" , derive(Serialize, Deserialize))] |
152 | pub enum PaintFlavor { |
153 | Color(Color), |
154 | #[cfg_attr (feature = "serde" , serde(skip))] |
155 | Image { |
156 | id: ImageId, |
157 | center: Position, |
158 | width: f32, |
159 | height: f32, |
160 | angle: f32, |
161 | tint: Color, |
162 | }, |
163 | LinearGradient { |
164 | start: Position, |
165 | end: Position, |
166 | colors: GradientColors, |
167 | }, |
168 | BoxGradient { |
169 | pos: Position, |
170 | width: f32, |
171 | height: f32, |
172 | radius: f32, |
173 | feather: f32, |
174 | colors: GradientColors, |
175 | }, |
176 | RadialGradient { |
177 | center: Position, |
178 | in_radius: f32, |
179 | out_radius: f32, |
180 | colors: GradientColors, |
181 | }, |
182 | } |
183 | |
184 | // Convenience method to fetch the GradientColors out of a PaintFlavor |
185 | impl PaintFlavor { |
186 | pub(crate) fn mul_alpha(&mut self, a: f32) { |
187 | match self { |
188 | Self::Color(color) => { |
189 | color.a *= a; |
190 | } |
191 | Self::Image { tint, .. } => { |
192 | tint.a *= a; |
193 | } |
194 | Self::LinearGradient { colors, .. } => { |
195 | colors.mul_alpha(a); |
196 | } |
197 | Self::BoxGradient { colors, .. } => { |
198 | colors.mul_alpha(a); |
199 | } |
200 | Self::RadialGradient { colors, .. } => { |
201 | colors.mul_alpha(a); |
202 | } |
203 | } |
204 | } |
205 | |
206 | pub(crate) fn gradient_colors(&self) -> Option<&GradientColors> { |
207 | match self { |
208 | Self::LinearGradient { colors, .. } => Some(colors), |
209 | Self::BoxGradient { colors, .. } => Some(colors), |
210 | Self::RadialGradient { colors, .. } => Some(colors), |
211 | _ => None, |
212 | } |
213 | } |
214 | |
215 | /// Returns true if this paint is an untransformed image paint without anti-aliasing at the edges in case of a fill |
216 | pub(crate) fn is_straight_tinted_image(&self, shape_anti_alias: bool) -> bool { |
217 | matches!(self, &Self::Image { angle, .. } if angle == 0.0 && !shape_anti_alias) |
218 | } |
219 | } |
220 | |
221 | #[derive (Copy, Clone, Debug, Default, PartialEq)] |
222 | pub enum GlyphTexture { |
223 | #[default] |
224 | None, |
225 | AlphaMask(ImageId), |
226 | ColorTexture(ImageId), |
227 | } |
228 | |
229 | impl GlyphTexture { |
230 | pub(crate) fn image_id(&self) -> Option<ImageId> { |
231 | match self { |
232 | GlyphTexture::None => None, |
233 | GlyphTexture::AlphaMask(image_id: &ImageId) | GlyphTexture::ColorTexture(image_id: &ImageId) => Some(*image_id), |
234 | } |
235 | } |
236 | } |
237 | |
238 | #[derive (Clone, Debug)] |
239 | #[cfg_attr (feature = "serde" , derive(Serialize, Deserialize))] |
240 | pub struct StrokeSettings { |
241 | pub(crate) stencil_strokes: bool, |
242 | pub(crate) miter_limit: f32, |
243 | pub(crate) line_width: f32, |
244 | pub(crate) line_cap_start: LineCap, |
245 | pub(crate) line_cap_end: LineCap, |
246 | pub(crate) line_join: LineJoin, |
247 | } |
248 | |
249 | impl Default for StrokeSettings { |
250 | fn default() -> Self { |
251 | Self { |
252 | stencil_strokes: true, |
253 | miter_limit: 10.0, |
254 | line_width: 1.0, |
255 | line_cap_start: Default::default(), |
256 | line_cap_end: Default::default(), |
257 | line_join: Default::default(), |
258 | } |
259 | } |
260 | } |
261 | |
262 | #[derive (Clone, Debug)] |
263 | #[cfg_attr (feature = "serde" , derive(Serialize, Deserialize))] |
264 | pub struct TextSettings { |
265 | #[cfg_attr (feature = "serde" , serde(skip))] |
266 | pub(crate) font_ids: [Option<FontId>; 8], |
267 | pub(crate) font_size: f32, |
268 | pub(crate) letter_spacing: f32, |
269 | pub(crate) text_baseline: Baseline, |
270 | pub(crate) text_align: Align, |
271 | } |
272 | |
273 | impl Default for TextSettings { |
274 | fn default() -> Self { |
275 | Self { |
276 | font_ids: Default::default(), |
277 | font_size: 16.0, |
278 | letter_spacing: 0.0, |
279 | text_baseline: Default::default(), |
280 | text_align: Default::default(), |
281 | } |
282 | } |
283 | } |
284 | |
285 | /// Struct controlling how graphical shapes are rendered. |
286 | /// |
287 | /// The Paint struct is a relatively lightweight object which contains all the information needed to |
288 | /// display something on the canvas. Unlike the HTML canvas where the current drawing style is stored |
289 | /// in an internal stack this paint struct is simply passed to the relevant drawing methods on the canvas. |
290 | /// |
291 | /// Clients code can have as many paints as they desire for different use cases and styles. This makes |
292 | /// the internal stack in the [Canvas](struct.Canvas.html) struct much lighter since it only needs to |
293 | /// contain the transform stack and current scissor rectangle. |
294 | /// |
295 | /// # Example |
296 | /// ``` |
297 | /// use femtovg::{Paint, Path, Color, Canvas, renderer::Void}; |
298 | /// |
299 | /// let mut canvas = Canvas::new(Void).expect("Cannot create canvas" ); |
300 | /// |
301 | /// let fill_paint = Paint::color(Color::hex("454545" )); |
302 | /// let stroke_paint = Paint::color(Color::hex("bababa" )).with_line_width(4.0); |
303 | /// |
304 | /// let mut path = Path::new(); |
305 | /// path.rounded_rect(10.0, 10.0, 100.0, 100.0, 20.0); |
306 | /// canvas.fill_path(&path, &fill_paint); |
307 | /// canvas.stroke_path(&path, &stroke_paint); |
308 | /// ``` |
309 | #[derive (Clone, Debug)] |
310 | #[cfg_attr (feature = "serde" , derive(Serialize, Deserialize))] |
311 | pub struct Paint { |
312 | pub(crate) flavor: PaintFlavor, |
313 | pub(crate) shape_anti_alias: bool, |
314 | pub(crate) stroke: StrokeSettings, |
315 | pub(crate) text: TextSettings, |
316 | pub(crate) fill_rule: FillRule, |
317 | } |
318 | |
319 | impl Default for Paint { |
320 | fn default() -> Self { |
321 | Self { |
322 | flavor: PaintFlavor::Color(Color::white()), |
323 | shape_anti_alias: true, |
324 | stroke: StrokeSettings::default(), |
325 | text: TextSettings::default(), |
326 | fill_rule: Default::default(), |
327 | } |
328 | } |
329 | } |
330 | |
331 | impl Paint { |
332 | /// Creates a new solid color paint |
333 | pub fn color(color: Color) -> Self { |
334 | Self::with_flavor(PaintFlavor::Color(color)) |
335 | } |
336 | |
337 | fn with_flavor(flavor: PaintFlavor) -> Self { |
338 | Self { |
339 | flavor, |
340 | ..Default::default() |
341 | } |
342 | } |
343 | |
344 | /// Creates a new image pattern paint. |
345 | /// |
346 | /// * `id` - is handle to the image to render |
347 | /// * `cx` `cy` - Specify the top-left location of the image pattern |
348 | /// * `width` `height` - The size of one image |
349 | /// * `angle` - Rotation around the top-left corner |
350 | /// * `alpha` - Transparency applied on the image |
351 | /// |
352 | /// # Example |
353 | /// ``` |
354 | /// use femtovg::{Paint, Path, Color, Canvas, ImageFlags, renderer::Void}; |
355 | /// |
356 | /// let mut canvas = Canvas::new(Void).expect("Cannot create canvas" ); |
357 | /// |
358 | /// let image_id = canvas.load_image_file("examples/assets/rust-logo.png" , ImageFlags::GENERATE_MIPMAPS).expect("Cannot create image" ); |
359 | /// let fill_paint = Paint::image(image_id, 10.0, 10.0, 85.0, 85.0, 0.0, 1.0); |
360 | /// |
361 | /// let mut path = Path::new(); |
362 | /// path.rect(10.0, 10.0, 85.0, 85.0); |
363 | /// canvas.fill_path(&path, &fill_paint); |
364 | /// ``` |
365 | pub fn image(id: ImageId, cx: f32, cy: f32, width: f32, height: f32, angle: f32, alpha: f32) -> Self { |
366 | Self::with_flavor(PaintFlavor::Image { |
367 | id, |
368 | center: Position { x: cx, y: cy }, |
369 | width, |
370 | height, |
371 | angle, |
372 | tint: Color::rgbaf(1.0, 1.0, 1.0, alpha), |
373 | }) |
374 | } |
375 | |
376 | /// Like `image`, but allows for adding a tint, or a color which will transform each pixel's |
377 | /// color via channel-wise multiplication. |
378 | pub fn image_tint(id: ImageId, cx: f32, cy: f32, width: f32, height: f32, angle: f32, tint: Color) -> Self { |
379 | Self::with_flavor(PaintFlavor::Image { |
380 | id, |
381 | center: Position { x: cx, y: cy }, |
382 | width, |
383 | height, |
384 | angle, |
385 | tint, |
386 | }) |
387 | } |
388 | |
389 | /// Creates and returns a linear gradient paint. |
390 | /// |
391 | /// The gradient is transformed by the current transform when it is passed to `fill_path()` or `stroke_path()`. |
392 | /// |
393 | /// # Example |
394 | /// ``` |
395 | /// use femtovg::{Paint, Path, Color, Canvas, ImageFlags, renderer::Void}; |
396 | /// |
397 | /// let mut canvas = Canvas::new(Void).expect("Cannot create canvas" ); |
398 | /// |
399 | /// let bg = Paint::linear_gradient(0.0, 0.0, 0.0, 100.0, Color::rgba(255, 255, 255, 16), Color::rgba(0, 0, 0, 16)); |
400 | /// let mut path = Path::new(); |
401 | /// path.rounded_rect(0.0, 0.0, 100.0, 100.0, 5.0); |
402 | /// canvas.fill_path(&path, &bg); |
403 | /// ``` |
404 | pub fn linear_gradient( |
405 | start_x: f32, |
406 | start_y: f32, |
407 | end_x: f32, |
408 | end_y: f32, |
409 | start_color: Color, |
410 | end_color: Color, |
411 | ) -> Self { |
412 | Self::with_flavor(PaintFlavor::LinearGradient { |
413 | start: Position { x: start_x, y: start_y }, |
414 | end: Position { x: end_x, y: end_y }, |
415 | colors: GradientColors::TwoStop { start_color, end_color }, |
416 | }) |
417 | } |
418 | /// Creates and returns a linear gradient paint with two or more stops. |
419 | /// |
420 | /// The gradient is transformed by the current transform when it is passed to `fill_path()` or `stroke_path()`. |
421 | /// |
422 | /// # Example |
423 | /// ``` |
424 | /// use femtovg::{Paint, Path, Color, Canvas, ImageFlags, renderer::Void}; |
425 | /// |
426 | /// let mut canvas = Canvas::new(Void).expect("Cannot create canvas" ); |
427 | /// |
428 | /// let bg = Paint::linear_gradient_stops( |
429 | /// 0.0, 0.0, |
430 | /// 0.0, 100.0, |
431 | /// [ |
432 | /// (0.0, Color::rgba(255, 255, 255, 16)), |
433 | /// (0.5, Color::rgba(0, 0, 0, 16)), |
434 | /// (1.0, Color::rgba(255, 0, 0, 16)) |
435 | /// ]); |
436 | /// let mut path = Path::new(); |
437 | /// path.rounded_rect(0.0, 0.0, 100.0, 100.0, 5.0); |
438 | /// canvas.fill_path(&path, &bg); |
439 | /// ``` |
440 | pub fn linear_gradient_stops( |
441 | start_x: f32, |
442 | start_y: f32, |
443 | end_x: f32, |
444 | end_y: f32, |
445 | stops: impl IntoIterator<Item = (f32, Color)>, |
446 | ) -> Self { |
447 | Self::with_flavor(PaintFlavor::LinearGradient { |
448 | start: Position { x: start_x, y: start_y }, |
449 | end: Position { x: end_x, y: end_y }, |
450 | colors: GradientColors::from_stops(stops), |
451 | }) |
452 | } |
453 | |
454 | #[allow (clippy::too_many_arguments)] |
455 | /// Creates and returns a box gradient. |
456 | /// |
457 | /// Box gradient is a feathered rounded rectangle, it is useful for rendering |
458 | /// drop shadows or highlights for boxes. Parameters (x,y) define the top-left corner of the rectangle, |
459 | /// (w,h) define the size of the rectangle, r defines the corner radius, and f feather. Feather defines how blurry |
460 | /// the border of the rectangle is. Parameter `inner_color` specifies the inner color and `outer_color` the outer color of the gradient. |
461 | /// The gradient is transformed by the current transform when it is passed to `fill_path()` or `stroke_path()`. |
462 | /// |
463 | /// # Example |
464 | /// ``` |
465 | /// use femtovg::{Paint, Path, Color, Canvas, ImageFlags, renderer::Void}; |
466 | /// |
467 | /// let mut canvas = Canvas::new(Void).expect("Cannot create canvas" ); |
468 | /// |
469 | /// let bg = Paint::box_gradient( |
470 | /// 0.0, |
471 | /// 0.0, |
472 | /// 100.0, |
473 | /// 100.0, |
474 | /// 10.0, |
475 | /// 10.0, |
476 | /// Color::rgba(0, 0, 0, 128), |
477 | /// Color::rgba(0, 0, 0, 0), |
478 | /// ); |
479 | /// |
480 | /// let mut path = Path::new(); |
481 | /// path.rounded_rect(0.0, 0.0, 100.0, 100.0, 5.0); |
482 | /// canvas.fill_path(&path, &bg); |
483 | /// ``` |
484 | pub fn box_gradient( |
485 | x: f32, |
486 | y: f32, |
487 | width: f32, |
488 | height: f32, |
489 | radius: f32, |
490 | feather: f32, |
491 | inner_color: Color, |
492 | outer_color: Color, |
493 | ) -> Self { |
494 | Self::with_flavor(PaintFlavor::BoxGradient { |
495 | pos: Position { x, y }, |
496 | width, |
497 | height, |
498 | radius, |
499 | feather, |
500 | colors: GradientColors::TwoStop { |
501 | start_color: inner_color, |
502 | end_color: outer_color, |
503 | }, |
504 | }) |
505 | } |
506 | |
507 | /// Creates and returns a radial gradient. |
508 | /// |
509 | /// Parameters (`cx`,`cy`) specify the center, `in_radius` and `out_radius` specify |
510 | /// the inner and outer radius of the gradient, `inner_color` specifies the start color and `outer_color` the end color. |
511 | /// The gradient is transformed by the current transform when it is passed to `fill_paint()` or `stroke_paint()`. |
512 | /// |
513 | /// # Example |
514 | /// ``` |
515 | /// use femtovg::{Paint, Path, Color, Canvas, ImageFlags, renderer::Void}; |
516 | /// |
517 | /// let mut canvas = Canvas::new(Void).expect("Cannot create canvas" ); |
518 | /// |
519 | /// let bg = Paint::radial_gradient( |
520 | /// 50.0, |
521 | /// 50.0, |
522 | /// 18.0, |
523 | /// 24.0, |
524 | /// Color::rgba(0, 0, 0, 128), |
525 | /// Color::rgba(0, 0, 0, 0), |
526 | /// ); |
527 | /// |
528 | /// let mut path = Path::new(); |
529 | /// path.circle(50.0, 50.0, 20.0); |
530 | /// canvas.fill_path(&path, &bg); |
531 | /// ``` |
532 | pub fn radial_gradient( |
533 | cx: f32, |
534 | cy: f32, |
535 | in_radius: f32, |
536 | out_radius: f32, |
537 | inner_color: Color, |
538 | outer_color: Color, |
539 | ) -> Self { |
540 | Self::with_flavor(PaintFlavor::RadialGradient { |
541 | center: Position { x: cx, y: cy }, |
542 | in_radius, |
543 | out_radius, |
544 | colors: GradientColors::TwoStop { |
545 | start_color: inner_color, |
546 | end_color: outer_color, |
547 | }, |
548 | }) |
549 | } |
550 | |
551 | /// Creates and returns a multi-stop radial gradient. |
552 | /// |
553 | /// Parameters (`cx`,`cy`) specify the center, `in_radius` and `out_radius` specify the inner and outer radius of the gradient, |
554 | /// colors specifies a list of color stops with offsets. The first offset should be 0.0 and the last offset should be 1.0. |
555 | /// |
556 | /// The gradient is transformed by the current transform when it is passed to `fill_paint()` or `stroke_paint()`. |
557 | /// |
558 | /// # Example |
559 | /// ``` |
560 | /// use femtovg::{Paint, Path, Color, Canvas, ImageFlags, renderer::Void}; |
561 | /// |
562 | /// let mut canvas = Canvas::new(Void).expect("Cannot create canvas" ); |
563 | /// |
564 | /// let bg = Paint::radial_gradient_stops( |
565 | /// 50.0, |
566 | /// 50.0, |
567 | /// 18.0, |
568 | /// 24.0, |
569 | /// [ |
570 | /// (0.0, Color::rgba(0, 0, 0, 128)), |
571 | /// (0.5, Color::rgba(0, 0, 128, 128)), |
572 | /// (1.0, Color::rgba(0, 128, 0, 128)) |
573 | /// ] |
574 | /// ); |
575 | /// |
576 | /// let mut path = Path::new(); |
577 | /// path.circle(50.0, 50.0, 20.0); |
578 | /// canvas.fill_path(&path, &bg); |
579 | /// ``` |
580 | pub fn radial_gradient_stops( |
581 | cx: f32, |
582 | cy: f32, |
583 | in_radius: f32, |
584 | out_radius: f32, |
585 | stops: impl IntoIterator<Item = (f32, Color)>, |
586 | ) -> Self { |
587 | Self::with_flavor(PaintFlavor::RadialGradient { |
588 | center: Position { x: cx, y: cy }, |
589 | in_radius, |
590 | out_radius, |
591 | colors: GradientColors::from_stops(stops), |
592 | }) |
593 | } |
594 | |
595 | /// Sets the color of the paint. |
596 | pub fn set_color(&mut self, color: Color) { |
597 | self.flavor = PaintFlavor::Color(color); |
598 | } |
599 | |
600 | /// Returns the paint with the color set to the specified value. |
601 | #[inline ] |
602 | pub fn with_color(mut self, color: Color) -> Self { |
603 | self.set_color(color); |
604 | self |
605 | } |
606 | |
607 | /// Returns the current anti-alias setting. |
608 | #[inline ] |
609 | pub fn anti_alias(&self) -> bool { |
610 | self.shape_anti_alias |
611 | } |
612 | |
613 | /// Sets whether shapes drawn with this paint will be anti-aliased. |
614 | #[inline ] |
615 | pub fn set_anti_alias(&mut self, value: bool) { |
616 | self.shape_anti_alias = value; |
617 | } |
618 | |
619 | /// Returns the paint with anti-alias set to the specified value. |
620 | #[inline ] |
621 | pub fn with_anti_alias(mut self, value: bool) -> Self { |
622 | self.set_anti_alias(value); |
623 | self |
624 | } |
625 | |
626 | /// Returns whether higher quality stencil strokes are used. |
627 | #[inline ] |
628 | pub fn stencil_strokes(&self) -> bool { |
629 | self.stroke.stencil_strokes |
630 | } |
631 | |
632 | /// Sets whether to use higher quality stencil strokes. |
633 | #[inline ] |
634 | pub fn set_stencil_strokes(&mut self, value: bool) { |
635 | self.stroke.stencil_strokes = value; |
636 | } |
637 | |
638 | /// Returns the paint with stencil strokes set to the specified value. |
639 | #[inline ] |
640 | pub fn with_stencil_strokes(mut self, value: bool) -> Self { |
641 | self.set_stencil_strokes(value); |
642 | self |
643 | } |
644 | |
645 | /// Returns the current line width. |
646 | #[inline ] |
647 | pub fn line_width(&self) -> f32 { |
648 | self.stroke.line_width |
649 | } |
650 | |
651 | /// Sets the line width. |
652 | #[inline ] |
653 | pub fn set_line_width(&mut self, width: f32) { |
654 | self.stroke.line_width = width; |
655 | } |
656 | |
657 | /// Returns the paint with line width set to the specified value. |
658 | #[inline ] |
659 | pub fn with_line_width(mut self, width: f32) -> Self { |
660 | self.set_line_width(width); |
661 | self |
662 | } |
663 | |
664 | /// Returns the current miter limit. |
665 | #[inline ] |
666 | pub fn miter_limit(&self) -> f32 { |
667 | self.stroke.miter_limit |
668 | } |
669 | |
670 | /// Sets the limit at which a sharp corner is drawn beveled. |
671 | /// |
672 | /// If the miter at a corner exceeds this limit, `LineJoin` is replaced with `LineJoin::Bevel`. |
673 | #[inline ] |
674 | pub fn set_miter_limit(&mut self, limit: f32) { |
675 | self.stroke.miter_limit = limit; |
676 | } |
677 | |
678 | /// Returns the paint with the miter limit set to the specified value. |
679 | #[inline ] |
680 | pub fn with_miter_limit(mut self, limit: f32) -> Self { |
681 | self.set_miter_limit(limit); |
682 | self |
683 | } |
684 | |
685 | /// Returns the current start line cap. |
686 | #[inline ] |
687 | pub fn line_cap_start(&self) -> LineCap { |
688 | self.stroke.line_cap_start |
689 | } |
690 | |
691 | /// Returns the current end line cap. |
692 | #[inline ] |
693 | pub fn line_cap_end(&self) -> LineCap { |
694 | self.stroke.line_cap_end |
695 | } |
696 | |
697 | /// Sets the line cap for both start and end of the line. |
698 | pub fn set_line_cap(&mut self, cap: LineCap) { |
699 | self.stroke.line_cap_start = cap; |
700 | self.stroke.line_cap_end = cap; |
701 | } |
702 | |
703 | /// Returns the paint with the line cap set to the specified value. |
704 | #[inline ] |
705 | pub fn with_line_cap(mut self, cap: LineCap) -> Self { |
706 | self.set_line_cap(cap); |
707 | self |
708 | } |
709 | |
710 | /// Sets the line cap for the start of the line. |
711 | #[inline ] |
712 | pub fn set_line_cap_start(&mut self, cap: LineCap) { |
713 | self.stroke.line_cap_start = cap; |
714 | } |
715 | |
716 | /// Returns the paint with the start line cap set to the specified value. |
717 | #[inline ] |
718 | pub fn with_line_cap_start(mut self, cap: LineCap) -> Self { |
719 | self.set_line_cap_start(cap); |
720 | self |
721 | } |
722 | |
723 | /// Sets the line cap for the end of the line. |
724 | #[inline ] |
725 | pub fn set_line_cap_end(&mut self, cap: LineCap) { |
726 | self.stroke.line_cap_end = cap; |
727 | } |
728 | |
729 | /// Returns the paint with the end line cap set to the specified value. |
730 | #[inline ] |
731 | pub fn with_line_cap_end(mut self, cap: LineCap) -> Self { |
732 | self.set_line_cap_end(cap); |
733 | self |
734 | } |
735 | |
736 | /// Returns the current line join. |
737 | #[inline ] |
738 | pub fn line_join(&self) -> LineJoin { |
739 | self.stroke.line_join |
740 | } |
741 | |
742 | /// Sets the line join. |
743 | #[inline ] |
744 | pub fn set_line_join(&mut self, join: LineJoin) { |
745 | self.stroke.line_join = join; |
746 | } |
747 | |
748 | /// Returns the paint with the line join set to the specified value. |
749 | #[inline ] |
750 | pub fn with_line_join(mut self, join: LineJoin) -> Self { |
751 | self.set_line_join(join); |
752 | self |
753 | } |
754 | |
755 | /// Sets the font. |
756 | pub fn set_font(&mut self, font_ids: &[FontId]) { |
757 | self.text.font_ids = Default::default(); |
758 | |
759 | for (i, id) in font_ids.iter().take(8).enumerate() { |
760 | self.text.font_ids[i] = Some(*id); |
761 | } |
762 | } |
763 | |
764 | /// Returns the paint with the font set to the specified value. |
765 | #[inline ] |
766 | pub fn with_font(mut self, font_ids: &[FontId]) -> Self { |
767 | self.set_font(font_ids); |
768 | self |
769 | } |
770 | |
771 | /// Returns the current font size for text operations. |
772 | #[inline ] |
773 | pub fn font_size(&self) -> f32 { |
774 | self.text.font_size |
775 | } |
776 | |
777 | /// Sets the font size for text operations. |
778 | #[inline ] |
779 | pub fn set_font_size(&mut self, size: f32) { |
780 | self.text.font_size = size; |
781 | } |
782 | |
783 | /// Returns the paint with the font size set to the specified value. |
784 | #[inline ] |
785 | pub fn with_font_size(mut self, size: f32) -> Self { |
786 | self.set_font_size(size); |
787 | self |
788 | } |
789 | |
790 | /// Returns the current letter spacing for text operations. |
791 | #[inline ] |
792 | pub fn letter_spacing(&self) -> f32 { |
793 | self.text.letter_spacing |
794 | } |
795 | |
796 | /// Sets the letter spacing for text operations. |
797 | #[inline ] |
798 | pub fn set_letter_spacing(&mut self, spacing: f32) { |
799 | self.text.letter_spacing = spacing; |
800 | } |
801 | |
802 | /// Returns the paint with the letter spacing set to the specified value. |
803 | #[inline ] |
804 | pub fn with_letter_spacing(mut self, spacing: f32) -> Self { |
805 | self.set_letter_spacing(spacing); |
806 | self |
807 | } |
808 | |
809 | /// Returns the current text baseline for text operations. |
810 | #[inline ] |
811 | pub fn text_baseline(&self) -> Baseline { |
812 | self.text.text_baseline |
813 | } |
814 | |
815 | /// Sets the text baseline for text operations. |
816 | #[inline ] |
817 | pub fn set_text_baseline(&mut self, align: Baseline) { |
818 | self.text.text_baseline = align; |
819 | } |
820 | |
821 | /// Returns the paint with the text baseline set to the specified value. |
822 | #[inline ] |
823 | pub fn with_text_baseline(mut self, align: Baseline) -> Self { |
824 | self.set_text_baseline(align); |
825 | self |
826 | } |
827 | |
828 | /// Returns the current text alignment for text operations. |
829 | #[inline ] |
830 | pub fn text_align(&self) -> Align { |
831 | self.text.text_align |
832 | } |
833 | |
834 | /// Sets the text alignment for text operations. |
835 | #[inline ] |
836 | pub fn set_text_align(&mut self, align: Align) { |
837 | self.text.text_align = align; |
838 | } |
839 | |
840 | /// Returns the paint with the text alignment set to the specified value. |
841 | #[inline ] |
842 | pub fn with_text_align(mut self, align: Align) -> Self { |
843 | self.set_text_align(align); |
844 | self |
845 | } |
846 | |
847 | /// Returns the current fill rule for filling paths. |
848 | #[inline ] |
849 | pub fn fill_rule(&self) -> FillRule { |
850 | self.fill_rule |
851 | } |
852 | |
853 | /// Sets the fill rule for filling paths. |
854 | #[inline ] |
855 | pub fn set_fill_rule(&mut self, rule: FillRule) { |
856 | self.fill_rule = rule; |
857 | } |
858 | |
859 | /// Returns the paint with the fill rule set to the specified value. |
860 | #[inline ] |
861 | pub fn with_fill_rule(mut self, rule: FillRule) -> Self { |
862 | self.set_fill_rule(rule); |
863 | self |
864 | } |
865 | } |
866 | |