1use crate::cache::PathCache;
2use crate::fonts::{FontId, Fonts, LayoutChar};
3use crate::renderer::{Renderer, Scissor, TextureType};
4use crate::{Color, Extent, NonaError, Point, Rect, Transform};
5use clamped::Clamp;
6use std::f32::consts::PI;
7
8pub type ImageId = usize;
9
10const KAPPA90: f32 = 0.5522847493;
11
12#[derive(Debug, Copy, Clone)]
13pub struct Paint {
14 pub xform: Transform,
15 pub extent: Extent,
16 pub radius: f32,
17 pub feather: f32,
18 pub inner_color: Color,
19 pub outer_color: Color,
20 pub image: Option<ImageId>,
21}
22
23#[derive(Debug, Copy, Clone)]
24pub enum Gradient {
25 Linear {
26 start: Point,
27 end: Point,
28 start_color: Color,
29 end_color: Color,
30 },
31 Radial {
32 center: Point,
33 in_radius: f32,
34 out_radius: f32,
35 inner_color: Color,
36 outer_color: Color,
37 },
38 Box {
39 rect: Rect,
40 radius: f32,
41 feather: f32,
42 inner_color: Color,
43 outer_color: Color,
44 },
45}
46
47#[derive(Debug, Copy, Clone)]
48pub struct ImagePattern {
49 pub center: Point,
50 pub size: Extent,
51 pub angle: f32,
52 pub img: ImageId,
53 pub alpha: f32,
54}
55
56impl From<Gradient> for Paint {
57 fn from(grad: Gradient) -> Self {
58 match grad {
59 Gradient::Linear {
60 start,
61 end,
62 start_color: inner_color,
63 end_color: outer_color,
64 } => {
65 const LARGE: f32 = 1e5;
66
67 let mut dx = end.x - start.x;
68 let mut dy = end.y - start.y;
69 let d = (dx * dx + dy * dy).sqrt();
70
71 if d > 0.0001 {
72 dx /= d;
73 dy /= d;
74 } else {
75 dx = 0.0;
76 dy = 1.0;
77 }
78
79 Paint {
80 xform: Transform([dy, -dx, dx, dy, start.x - dx * LARGE, start.y - dy * LARGE]),
81 extent: Extent {
82 width: LARGE,
83 height: LARGE + d * 0.5,
84 },
85 radius: 0.0,
86 feather: d.max(1.0),
87 inner_color,
88 outer_color,
89 image: None,
90 }
91 }
92 Gradient::Radial {
93 center,
94 in_radius,
95 out_radius,
96 inner_color,
97 outer_color,
98 } => {
99 let r = (in_radius + out_radius) * 0.5;
100 let f = out_radius - in_radius;
101 Paint {
102 xform: Transform([1.0, 0.0, 0.0, 1.0, center.x, center.y]),
103 extent: Extent {
104 width: r,
105 height: r,
106 },
107 radius: r,
108 feather: f.max(1.0),
109 inner_color,
110 outer_color,
111 image: None,
112 }
113 }
114 Gradient::Box {
115 rect,
116 radius,
117 feather,
118 inner_color,
119 outer_color,
120 } => {
121 let Rect { xy, size } = rect;
122 Paint {
123 xform: Transform([
124 1.0,
125 0.0,
126 0.0,
127 1.0,
128 xy.x + size.width * 0.5,
129 xy.y + size.height * 0.5,
130 ]),
131 extent: Extent::new(size.width * 0.5, size.height * 0.5),
132 radius,
133 feather: feather.max(1.0),
134 inner_color,
135 outer_color,
136 image: None,
137 }
138 }
139 }
140 }
141}
142
143impl From<ImagePattern> for Paint {
144 fn from(pat: ImagePattern) -> Self {
145 let mut xform = Transform::rotate(pat.angle);
146 xform.0[4] = pat.center.x;
147 xform.0[5] = pat.center.y;
148 Paint {
149 xform,
150 extent: pat.size,
151 radius: 0.0,
152 feather: 0.0,
153 inner_color: Color::rgba(1.0, 1.0, 1.0, pat.alpha),
154 outer_color: Color::rgba(1.0, 1.0, 1.0, pat.alpha),
155 image: Some(pat.img),
156 }
157 }
158}
159
160impl<T: Into<Color> + Clone> From<T> for Paint {
161 fn from(color: T) -> Self {
162 Paint {
163 xform: Transform::identity(),
164 extent: Default::default(),
165 radius: 0.0,
166 feather: 1.0,
167 inner_color: color.clone().into(),
168 outer_color: color.into(),
169 image: None,
170 }
171 }
172}
173
174#[derive(Debug, Copy, Clone, Eq, PartialEq)]
175pub enum Solidity {
176 Solid,
177 Hole,
178}
179
180#[derive(Debug, Copy, Clone, Eq, PartialEq)]
181pub enum LineJoin {
182 Miter,
183 Round,
184 Bevel,
185}
186
187#[derive(Debug, Copy, Clone, Eq, PartialEq)]
188pub enum LineCap {
189 Butt,
190 Round,
191 Square,
192}
193
194bitflags! {
195 pub struct Align: u32 {
196 const LEFT = 0x1;
197 const CENTER = 0x2;
198 const RIGHT = 0x4;
199 const TOP = 0x8;
200 const MIDDLE = 0x10;
201 const BOTTOM = 0x20;
202 const BASELINE = 0x40;
203 }
204}
205
206#[derive(Debug, Copy, Clone)]
207pub enum BlendFactor {
208 Zero,
209 One,
210 SrcColor,
211 OneMinusSrcColor,
212 DstColor,
213 OneMinusDstColor,
214 SrcAlpha,
215 OneMinusSrcAlpha,
216 DstAlpha,
217 OneMinusDstAlpha,
218 SrcAlphaSaturate,
219}
220
221#[derive(Debug, Copy, Clone)]
222pub enum BasicCompositeOperation {
223 SrcOver,
224 SrcIn,
225 SrcOut,
226 Atop,
227 DstOver,
228 DstIn,
229 DstOut,
230 DstAtop,
231 Lighter,
232 Copy,
233 Xor,
234}
235
236#[derive(Debug, Copy, Clone)]
237pub enum CompositeOperation {
238 Basic(BasicCompositeOperation),
239 BlendFunc {
240 src: BlendFactor,
241 dst: BlendFactor,
242 },
243 BlendFuncSeparate {
244 src_rgb: BlendFactor,
245 dst_rgb: BlendFactor,
246 src_alpha: BlendFactor,
247 dst_alpha: BlendFactor,
248 },
249}
250
251impl Into<CompositeOperationState> for CompositeOperation {
252 fn into(self) -> CompositeOperationState {
253 match self {
254 CompositeOperation::Basic(op) => {
255 let (src_factor, dst_factor) = match op {
256 BasicCompositeOperation::SrcOver => {
257 (BlendFactor::One, BlendFactor::OneMinusSrcAlpha)
258 }
259 BasicCompositeOperation::SrcIn => (BlendFactor::DstAlpha, BlendFactor::Zero),
260 BasicCompositeOperation::SrcOut => {
261 (BlendFactor::OneMinusDstAlpha, BlendFactor::Zero)
262 }
263 BasicCompositeOperation::Atop => {
264 (BlendFactor::DstAlpha, BlendFactor::OneMinusSrcAlpha)
265 }
266 BasicCompositeOperation::DstOver => {
267 (BlendFactor::OneMinusDstAlpha, BlendFactor::One)
268 }
269 BasicCompositeOperation::DstIn => (BlendFactor::Zero, BlendFactor::SrcAlpha),
270 BasicCompositeOperation::DstOut => {
271 (BlendFactor::Zero, BlendFactor::OneMinusSrcAlpha)
272 }
273 BasicCompositeOperation::DstAtop => {
274 (BlendFactor::OneMinusDstAlpha, BlendFactor::SrcAlpha)
275 }
276 BasicCompositeOperation::Lighter => (BlendFactor::One, BlendFactor::One),
277 BasicCompositeOperation::Copy => (BlendFactor::One, BlendFactor::Zero),
278 BasicCompositeOperation::Xor => {
279 (BlendFactor::OneMinusDstAlpha, BlendFactor::OneMinusSrcAlpha)
280 }
281 };
282
283 CompositeOperationState {
284 src_rgb: src_factor,
285 dst_rgb: dst_factor,
286 src_alpha: src_factor,
287 dst_alpha: dst_factor,
288 }
289 }
290 CompositeOperation::BlendFunc { src, dst } => CompositeOperationState {
291 src_rgb: src,
292 dst_rgb: dst,
293 src_alpha: src,
294 dst_alpha: dst,
295 },
296 CompositeOperation::BlendFuncSeparate {
297 src_rgb,
298 dst_rgb,
299 src_alpha,
300 dst_alpha,
301 } => CompositeOperationState {
302 src_rgb,
303 dst_rgb,
304 src_alpha,
305 dst_alpha,
306 },
307 }
308 }
309}
310
311#[derive(Debug, Copy, Clone)]
312pub struct CompositeOperationState {
313 pub src_rgb: BlendFactor,
314 pub dst_rgb: BlendFactor,
315 pub src_alpha: BlendFactor,
316 pub dst_alpha: BlendFactor,
317}
318
319bitflags! {
320 pub struct ImageFlags: u32 {
321 const GENERATE_MIPMAPS = 0x1;
322 const REPEATX = 0x2;
323 const REPEATY = 0x4;
324 const FLIPY = 0x8;
325 const PREMULTIPLIED = 0x10;
326 const NEAREST = 0x20;
327 }
328}
329
330#[derive(Debug, Copy, Clone, Default)]
331pub struct Vertex {
332 pub x: f32,
333 pub y: f32,
334 pub u: f32,
335 pub v: f32,
336}
337
338impl Vertex {
339 pub fn new(x: f32, y: f32, u: f32, v: f32) -> Vertex {
340 Vertex { x, y, u, v }
341 }
342}
343
344#[derive(Debug, Copy, Clone)]
345pub struct Path {
346 pub(crate) first: usize,
347 pub(crate) count: usize,
348 pub(crate) closed: bool,
349 pub(crate) num_bevel: usize,
350 pub(crate) solidity: Solidity,
351 pub(crate) fill: *mut Vertex,
352 pub(crate) num_fill: usize,
353 pub(crate) stroke: *mut Vertex,
354 pub(crate) num_stroke: usize,
355 pub convex: bool,
356}
357
358impl Path {
359 pub fn get_fill(&self) -> &[Vertex] {
360 if self.fill.is_null() {
361 &[]
362 } else {
363 unsafe { std::slice::from_raw_parts_mut(self.fill, self.num_fill) }
364 }
365 }
366
367 pub fn get_stroke(&self) -> &[Vertex] {
368 if self.stroke.is_null() {
369 &[]
370 } else {
371 unsafe { std::slice::from_raw_parts_mut(self.stroke, self.num_stroke) }
372 }
373 }
374}
375
376#[derive(Copy, Clone)]
377pub struct TextMetrics {
378 pub ascender: f32,
379 pub descender: f32,
380 pub line_gap: f32,
381}
382
383impl TextMetrics {
384 pub fn line_height(&self) -> f32 {
385 self.ascender - self.descender + self.line_gap
386 }
387}
388
389#[derive(Clone)]
390struct State {
391 composite_operation: CompositeOperationState,
392 shape_antialias: bool,
393 fill: Paint,
394 stroke: Paint,
395 stroke_width: f32,
396 miter_limit: f32,
397 line_join: LineJoin,
398 line_cap: LineCap,
399 alpha: f32,
400 xform: Transform,
401 scissor: Scissor,
402 font_size: f32,
403 letter_spacing: f32,
404 line_height: f32,
405 text_align: Align,
406 font_id: FontId,
407}
408
409impl Default for State {
410 fn default() -> Self {
411 State {
412 composite_operation: CompositeOperation::Basic(BasicCompositeOperation::SrcOver).into(),
413 shape_antialias: true,
414 fill: Color::rgb(1.0, 1.0, 1.0).into(),
415 stroke: Color::rgb(0.0, 0.0, 0.0).into(),
416 stroke_width: 1.0,
417 miter_limit: 10.0,
418 line_join: LineJoin::Miter,
419 line_cap: LineCap::Butt,
420 alpha: 1.0,
421 xform: Transform::identity(),
422 scissor: Scissor {
423 xform: Default::default(),
424 extent: Extent {
425 width: -1.0,
426 height: -1.0,
427 },
428 },
429 font_size: 16.0,
430 letter_spacing: 0.0,
431 line_height: 1.0,
432 text_align: Align::LEFT | Align::BASELINE,
433 font_id: 0,
434 }
435 }
436}
437
438#[derive(Debug)]
439pub(crate) enum Command {
440 MoveTo(Point),
441 LineTo(Point),
442 BezierTo(Point, Point, Point),
443 Close,
444 Solidity(Solidity),
445}
446
447pub struct Context {
448 commands: Vec<Command>,
449 last_position: Point,
450 states: Vec<State>,
451 cache: PathCache,
452 tess_tol: f32,
453 dist_tol: f32,
454 fringe_width: f32,
455 device_pixel_ratio: f32,
456 fonts: Fonts,
457 layout_chars: Vec<LayoutChar>,
458 draw_call_count: usize,
459 fill_triangles_count: usize,
460 stroke_triangles_count: usize,
461 text_triangles_count: usize,
462}
463
464pub struct Canvas<'a, R: Renderer> {
465 context: &'a mut Context,
466 renderer: &'a mut R,
467}
468
469impl<'a, R: Renderer> std::ops::Deref for Canvas<'a, R> {
470 type Target = Context;
471
472 fn deref(&self) -> &Self::Target {
473 self.context
474 }
475}
476impl<R: Renderer> std::ops::DerefMut for Canvas<'_, R> {
477 fn deref_mut(&mut self) -> &mut Self::Target {
478 self.context
479 }
480}
481
482impl<'a, R: Renderer> Canvas<'a, R> {
483 pub fn begin_frame(&mut self, clear_color: Option<Color>) -> Result<(), NonaError> {
484 self.context.begin_frame(self.renderer, clear_color)
485 }
486
487 pub fn end_frame(&mut self) -> Result<(), NonaError> {
488 self.context.end_frame(self.renderer)
489 }
490
491 pub fn create_image<D: AsRef<[u8]>>(
492 &mut self,
493 flags: ImageFlags,
494 data: D,
495 ) -> Result<ImageId, NonaError> {
496 self.context.create_image(self.renderer, flags, data)
497 }
498
499 pub fn update_image(&mut self, img: ImageId, data: &[u8]) -> Result<(), NonaError> {
500 self.context.update_image(self.renderer, img, data)
501 }
502
503 pub fn image_size(&self, img: ImageId) -> Result<(usize, usize), NonaError> {
504 self.context.image_size(self.renderer, img)
505 }
506
507 pub fn delete_image(&mut self, img: ImageId) -> Result<(), NonaError> {
508 self.context.delete_image(self.renderer, img)
509 }
510
511 pub fn fill(&mut self) -> Result<(), NonaError> {
512 self.context.fill(self.renderer)
513 }
514
515 pub fn stroke(&mut self) -> Result<(), NonaError> {
516 self.context.stroke(self.renderer)
517 }
518
519 pub fn text<S: AsRef<str>, P: Into<Point>>(&mut self, pt: P, text: S) -> Result<(), NonaError> {
520 self.context.text(self.renderer, pt, text)
521 }
522}
523
524impl Context {
525 pub fn create<R: Renderer>(renderer: &mut R) -> Result<Context, NonaError> {
526 let fonts = Fonts::new(renderer)?;
527 Ok(Context {
528 commands: Default::default(),
529 last_position: Default::default(),
530 states: vec![Default::default()],
531 cache: Default::default(),
532 tess_tol: 0.0,
533 dist_tol: 0.0,
534 fringe_width: 0.0,
535 device_pixel_ratio: 0.0,
536 fonts,
537 layout_chars: Default::default(),
538 draw_call_count: 0,
539 fill_triangles_count: 0,
540 stroke_triangles_count: 0,
541 text_triangles_count: 0,
542 })
543 }
544
545 fn set_device_pixel_ratio(&mut self, ratio: f32) {
546 self.tess_tol = 0.25 / ratio;
547 self.dist_tol = 0.01 / ratio;
548 self.fringe_width = 1.0 / ratio;
549 self.device_pixel_ratio = ratio;
550 }
551
552 pub fn attach_renderer<R: Renderer>(
553 &mut self,
554 renderer: &mut R,
555 call: impl Fn(&mut Canvas<R>),
556 ) {
557 let mut canvas = Canvas {
558 context: self,
559 renderer,
560 };
561 call(&mut canvas)
562 }
563
564 pub fn begin_frame<R: Renderer>(
565 &mut self,
566 renderer: &mut R,
567 clear_color: Option<Color>,
568 ) -> Result<(), NonaError> {
569 let device_pixel_ratio = {
570 renderer.viewport(renderer.view_size().into(), renderer.device_pixel_ratio())?;
571 if let Some(color) = clear_color {
572 renderer.clear_screen(color)
573 }
574 renderer.device_pixel_ratio()
575 };
576 self.set_device_pixel_ratio(device_pixel_ratio);
577 self.states.clear();
578 self.states.push(Default::default());
579 self.draw_call_count = 0;
580 self.fill_triangles_count = 0;
581 self.stroke_triangles_count = 0;
582 self.text_triangles_count = 0;
583 Ok(())
584 }
585
586 pub fn end_frame<R: Renderer>(&mut self, renderer: &mut R) -> Result<(), NonaError> {
587 renderer.flush()
588 }
589
590 pub fn save(&mut self) {
591 if let Some(last) = self.states.last() {
592 let last = last.clone();
593 self.states.push(last);
594 }
595 }
596
597 pub fn restore(&mut self) {
598 if self.states.len() <= 1 {
599 return;
600 }
601 self.states.pop();
602 }
603
604 fn state(&mut self) -> &State {
605 self.states.last().unwrap()
606 }
607
608 fn state_mut(&mut self) -> &mut State {
609 self.states.last_mut().unwrap()
610 }
611
612 pub fn reset(&mut self) {
613 *self.state_mut() = Default::default();
614 }
615
616 pub fn shape_antialias(&mut self, enabled: bool) {
617 self.state_mut().shape_antialias = enabled;
618 }
619
620 pub fn stroke_width(&mut self, width: f32) {
621 self.state_mut().stroke_width = width;
622 }
623
624 pub fn miter_limit(&mut self, limit: f32) {
625 self.state_mut().miter_limit = limit;
626 }
627
628 pub fn line_cap(&mut self, cap: LineCap) {
629 self.state_mut().line_cap = cap;
630 }
631
632 pub fn line_join(&mut self, join: LineJoin) {
633 self.state_mut().line_join = join;
634 }
635
636 pub fn global_alpha(&mut self, alpha: f32) {
637 self.state_mut().alpha = alpha;
638 }
639
640 pub fn transform(&mut self, xform: Transform) {
641 let state = self.state_mut();
642 state.xform = xform * state.xform;
643 }
644
645 pub fn reset_transform(&mut self) {
646 self.state_mut().xform = Transform::identity();
647 }
648
649 pub fn translate(&mut self, tx: f32, ty: f32) {
650 self.transform(Transform::translate(tx, ty));
651 }
652
653 pub fn rotate(&mut self, angle: f32) {
654 self.transform(Transform::rotate(angle));
655 }
656
657 pub fn skew_x(&mut self, angle: f32) {
658 self.transform(Transform::skew_x(angle));
659 }
660
661 pub fn skew_y(&mut self, angle: f32) {
662 self.transform(Transform::skew_y(angle));
663 }
664
665 pub fn scale(&mut self, sx: f32, sy: f32) {
666 self.transform(Transform::scale(sx, sy));
667 }
668
669 pub fn current_transform(&mut self) -> Transform {
670 self.state().xform
671 }
672
673 pub fn stroke_paint<T: Into<Paint>>(&mut self, paint: T) {
674 let mut paint = paint.into();
675 paint.xform *= self.state().xform;
676 self.state_mut().stroke = paint;
677 }
678
679 pub fn fill_paint<T: Into<Paint>>(&mut self, paint: T) {
680 let mut paint = paint.into();
681 paint.xform *= self.state().xform;
682 self.state_mut().fill = paint;
683 }
684
685 pub fn create_image<D: AsRef<[u8]>, R: Renderer>(
686 &mut self,
687 renderer: &mut R,
688 flags: ImageFlags,
689 data: D,
690 ) -> Result<ImageId, NonaError> {
691 let img = image::load_from_memory(data.as_ref())
692 .map_err(|err| NonaError::Texture(err.to_string()))?;
693 let img = img.to_rgba8();
694 let dimensions = img.dimensions();
695 let img = renderer.create_texture(
696 TextureType::RGBA,
697 dimensions.0 as usize,
698 dimensions.1 as usize,
699 flags,
700 Some(&img.into_raw()),
701 )?;
702 Ok(img)
703 }
704
705 pub fn create_image_from_file<P: AsRef<std::path::Path>, R: Renderer>(
706 &mut self,
707 renderer: &mut R,
708 flags: ImageFlags,
709 path: P,
710 ) -> Result<ImageId, NonaError> {
711 self.create_image(
712 renderer,
713 flags,
714 std::fs::read(path)
715 .map_err(|err| NonaError::Texture(format!("Error loading image: {}", err)))?,
716 )
717 }
718
719 pub fn update_image<R: Renderer>(
720 &mut self,
721 renderer: &mut R,
722 img: ImageId,
723 data: &[u8],
724 ) -> Result<(), NonaError> {
725 let (w, h) = renderer.texture_size(img.clone())?;
726 renderer.update_texture(img, 0, 0, w, h, data)?;
727 Ok(())
728 }
729
730 pub fn image_size<R: Renderer>(
731 &self,
732 renderer: &R,
733 img: ImageId,
734 ) -> Result<(usize, usize), NonaError> {
735 let res = renderer.texture_size(img)?;
736 Ok(res)
737 }
738
739 pub fn delete_image<R: Renderer>(
740 &mut self,
741 renderer: &mut R,
742 img: ImageId,
743 ) -> Result<(), NonaError> {
744 renderer.delete_texture(img)?;
745 Ok(())
746 }
747
748 pub fn scissor<T: Into<Rect>>(&mut self, rect: T) {
749 let rect = rect.into();
750 let state = self.state_mut();
751 let x = rect.xy.x;
752 let y = rect.xy.y;
753 let width = rect.size.width.max(0.0);
754 let height = rect.size.height.max(0.0);
755 state.scissor.xform = Transform::identity();
756 state.scissor.xform.0[4] = x + width * 0.5;
757 state.scissor.xform.0[5] = y + height * 0.5;
758 state.scissor.xform *= state.xform;
759 state.scissor.extent.width = width * 0.5;
760 state.scissor.extent.height = height * 0.5;
761 }
762
763 pub fn intersect_scissor<T: Into<Rect>>(&mut self, rect: T) {
764 let rect = rect.into();
765 let state = self.state_mut();
766
767 if state.scissor.extent.width < 0.0 {
768 self.scissor(rect);
769 return;
770 }
771
772 let Extent {
773 width: ex,
774 height: ey,
775 } = state.scissor.extent;
776 let invxorm = state.xform.inverse();
777 let pxform = state.scissor.xform * invxorm;
778 let tex = ex * pxform.0[0].abs() + ey * pxform.0[2].abs();
779 let tey = ex * pxform.0[1].abs() + ey * pxform.0[3].abs();
780 self.scissor(
781 Rect::new(
782 Point::new(pxform.0[4] - tex, pxform.0[5] - tey),
783 Extent::new(tex * 2.0, tey * 2.0),
784 )
785 .intersect(rect),
786 );
787 }
788
789 pub fn reset_scissor(&mut self) {
790 let state = self.state_mut();
791 state.scissor.xform = Transform::default();
792 state.scissor.extent.width = -1.0;
793 state.scissor.extent.height = -1.0;
794 }
795
796 pub fn global_composite_operation(&mut self, op: CompositeOperation) {
797 self.state_mut().composite_operation = op.into();
798 }
799
800 fn append_command(&mut self, cmd: Command) {
801 let state = self.states.last().unwrap();
802 let xform = &state.xform;
803 match cmd {
804 Command::MoveTo(pt) => {
805 self.commands
806 .push(Command::MoveTo(xform.transform_point(pt)));
807 self.last_position = pt;
808 }
809 Command::LineTo(pt) => {
810 self.commands
811 .push(Command::LineTo(xform.transform_point(pt)));
812 self.last_position = pt;
813 }
814 Command::BezierTo(pt1, pt2, pt3) => {
815 self.last_position = pt3;
816 self.commands.push(Command::BezierTo(
817 xform.transform_point(pt1),
818 xform.transform_point(pt2),
819 xform.transform_point(pt3),
820 ));
821 }
822 _ => {
823 self.commands.push(cmd);
824 }
825 }
826 }
827
828 pub fn begin_path(&mut self) {
829 self.commands.clear();
830 self.cache.clear();
831 }
832
833 pub fn move_to<P: Into<Point>>(&mut self, pt: P) {
834 self.append_command(Command::MoveTo(pt.into()));
835 }
836
837 pub fn line_to<P: Into<Point>>(&mut self, pt: P) {
838 self.append_command(Command::LineTo(pt.into()));
839 }
840
841 pub fn bezier_to<P: Into<Point>>(&mut self, cp1: P, cp2: P, pt: P) {
842 self.append_command(Command::BezierTo(cp1.into(), cp2.into(), pt.into()));
843 }
844
845 pub fn quad_to<P: Into<Point>>(&mut self, cp: P, pt: P) {
846 let x0 = self.last_position.x;
847 let y0 = self.last_position.y;
848 let cp = cp.into();
849 let pt = pt.into();
850 self.append_command(Command::BezierTo(
851 Point::new(x0 + 2.0 / 3.0 * (cp.x - x0), y0 + 2.0 / 3.0 * (cp.y - y0)),
852 Point::new(
853 pt.x + 2.0 / 3.0 * (cp.x - pt.x),
854 pt.y + 2.0 / 3.0 * (cp.y - pt.y),
855 ),
856 pt,
857 ));
858 }
859
860 pub fn arc_to<P: Into<Point>>(&mut self, pt1: P, pt2: P, radius: f32) {
861 let pt0 = self.last_position;
862
863 if self.commands.is_empty() {
864 return;
865 }
866
867 let pt1 = pt1.into();
868 let pt2 = pt2.into();
869 if pt0.equals(pt1, self.dist_tol)
870 || pt1.equals(pt2, self.dist_tol)
871 || pt1.dist_pt_seg(pt0, pt2) < self.dist_tol * self.dist_tol
872 || radius < self.dist_tol
873 {
874 self.line_to(pt1);
875 return;
876 }
877
878 let d0 = Point::new(pt0.x - pt1.x, pt0.y - pt1.y);
879 let d1 = Point::new(pt2.x - pt1.x, pt2.y - pt1.y);
880 let a = (d0.x * d1.x + d0.y * d1.y).cos();
881 let d = radius / (a / 2.0).tan();
882
883 if d > 10000.0 {
884 self.line_to(pt1);
885 return;
886 }
887
888 let (cx, cy, a0, a1, dir) = if Point::cross(d0, d1) > 0.0 {
889 (
890 pt1.x + d0.x * d + d0.y * radius,
891 pt1.y + d0.y * d + -d0.x * radius,
892 d0.x.atan2(-d0.y),
893 -d1.x.atan2(d1.y),
894 Solidity::Hole,
895 )
896 } else {
897 (
898 pt1.x + d0.x * d + -d0.y * radius,
899 pt1.y + d0.y * d + d0.x * radius,
900 -d0.x.atan2(d0.y),
901 d1.x.atan2(-d1.y),
902 Solidity::Solid,
903 )
904 };
905
906 self.arc(Point::new(cx, cy), radius, a0, a1, dir);
907 }
908
909 pub fn close_path(&mut self) {
910 self.commands.push(Command::Close);
911 }
912
913 pub fn path_solidity(&mut self, dir: Solidity) {
914 self.commands.push(Command::Solidity(dir));
915 }
916
917 pub fn arc<P: Into<Point>>(&mut self, cp: P, radius: f32, a0: f32, a1: f32, dir: Solidity) {
918 let cp = cp.into();
919 let move_ = self.commands.is_empty();
920
921 let mut da = a1 - a0;
922 if dir == Solidity::Hole {
923 if da.abs() >= PI * 2.0 {
924 da = PI * 2.0;
925 } else {
926 while da < 0.0 {
927 da += PI * 2.0;
928 }
929 }
930 } else {
931 if da.abs() >= PI * 2.0 {
932 da = -PI * 2.0;
933 } else {
934 while da > 0.0 {
935 da -= PI * 2.0;
936 }
937 }
938 }
939
940 let ndivs = ((da.abs() / (PI * 0.5) + 0.5) as i32).min(5).max(1);
941 let hda = (da / (ndivs as f32)) / 2.0;
942 let mut kappa = (4.0 / 3.0 * (1.0 - hda.cos()) / hda.sin()).abs();
943
944 if dir == Solidity::Solid {
945 kappa = -kappa;
946 }
947
948 let mut px = 0.0;
949 let mut py = 0.0;
950 let mut ptanx = 0.0;
951 let mut ptany = 0.0;
952
953 for i in 0..=ndivs {
954 let a = a0 + da * ((i as f32) / (ndivs as f32));
955 let dx = a.cos();
956 let dy = a.sin();
957 let x = cp.x + dx * radius;
958 let y = cp.y + dy * radius;
959 let tanx = -dy * radius * kappa;
960 let tany = dx * radius * kappa;
961
962 if i == 0 {
963 if move_ {
964 self.append_command(Command::MoveTo(Point::new(x, y)));
965 } else {
966 self.append_command(Command::LineTo(Point::new(x, y)));
967 }
968 } else {
969 self.append_command(Command::BezierTo(
970 Point::new(px + ptanx, py + ptany),
971 Point::new(x - tanx, y - tany),
972 Point::new(x, y),
973 ));
974 }
975 px = x;
976 py = y;
977 ptanx = tanx;
978 ptany = tany;
979 }
980 }
981
982 pub fn rect<T: Into<Rect>>(&mut self, rect: T) {
983 let rect = rect.into();
984 self.append_command(Command::MoveTo(Point::new(rect.xy.x, rect.xy.y)));
985 self.append_command(Command::LineTo(Point::new(
986 rect.xy.x,
987 rect.xy.y + rect.size.height,
988 )));
989 self.append_command(Command::LineTo(Point::new(
990 rect.xy.x + rect.size.width,
991 rect.xy.y + rect.size.height,
992 )));
993 self.append_command(Command::LineTo(Point::new(
994 rect.xy.x + rect.size.width,
995 rect.xy.y,
996 )));
997 self.append_command(Command::Close);
998 }
999
1000 pub fn rounded_rect<T: Into<Rect>>(&mut self, rect: T, radius: f32) {
1001 let rect = rect.into();
1002 self.rounded_rect_varying(rect, radius, radius, radius, radius);
1003 }
1004
1005 pub fn rounded_rect_varying<T: Into<Rect>>(
1006 &mut self,
1007 rect: T,
1008 lt: f32,
1009 rt: f32,
1010 rb: f32,
1011 lb: f32,
1012 ) {
1013 let rect = rect.into();
1014 if lt < 0.1 && rt < 0.1 && lb < 0.1 && rb < 0.1 {
1015 self.rect(rect);
1016 } else {
1017 let halfw = rect.size.width.abs() * 0.5;
1018 let halfh = rect.size.height.abs() * 0.5;
1019 let rxlb = lb.min(halfw) * rect.size.width.signum();
1020 let rylb = lb.min(halfh) * rect.size.height.signum();
1021 let rxrb = rb.min(halfw) * rect.size.width.signum();
1022 let ryrb = rb.min(halfh) * rect.size.height.signum();
1023 let rxrt = rt.min(halfw) * rect.size.width.signum();
1024 let ryrt = rt.min(halfh) * rect.size.height.signum();
1025 let rxlt = lt.min(halfw) * rect.size.width.signum();
1026 let rylt = lt.min(halfh) * rect.size.height.signum();
1027
1028 self.append_command(Command::MoveTo(Point::new(rect.xy.x, rect.xy.y + rylt)));
1029 self.append_command(Command::LineTo(Point::new(
1030 rect.xy.x,
1031 rect.xy.y + rect.size.height - rylb,
1032 )));
1033 self.append_command(Command::BezierTo(
1034 Point::new(
1035 rect.xy.x,
1036 rect.xy.y + rect.size.height - rylb * (1.0 - KAPPA90),
1037 ),
1038 Point::new(
1039 rect.xy.x + rxlb * (1.0 - KAPPA90),
1040 rect.xy.y + rect.size.height,
1041 ),
1042 Point::new(rect.xy.x + rxlb, rect.xy.y + rect.size.height),
1043 ));
1044 self.append_command(Command::LineTo(Point::new(
1045 rect.xy.x + rect.size.width - rxrb,
1046 rect.xy.y + rect.size.height,
1047 )));
1048 self.append_command(Command::BezierTo(
1049 Point::new(
1050 rect.xy.x + rect.size.width - rxrb * (1.0 - KAPPA90),
1051 rect.xy.y + rect.size.height,
1052 ),
1053 Point::new(
1054 rect.xy.x + rect.size.width,
1055 rect.xy.y + rect.size.height - ryrb * (1.0 - KAPPA90),
1056 ),
1057 Point::new(
1058 rect.xy.x + rect.size.width,
1059 rect.xy.y + rect.size.height - ryrb,
1060 ),
1061 ));
1062 self.append_command(Command::LineTo(Point::new(
1063 rect.xy.x + rect.size.width,
1064 rect.xy.y + ryrt,
1065 )));
1066 self.append_command(Command::BezierTo(
1067 Point::new(
1068 rect.xy.x + rect.size.width,
1069 rect.xy.y + ryrt * (1.0 - KAPPA90),
1070 ),
1071 Point::new(
1072 rect.xy.x + rect.size.width - rxrt * (1.0 - KAPPA90),
1073 rect.xy.y,
1074 ),
1075 Point::new(rect.xy.x + rect.size.width - rxrt, rect.xy.y),
1076 ));
1077 self.append_command(Command::LineTo(Point::new(rect.xy.x + rxlt, rect.xy.y)));
1078 self.append_command(Command::BezierTo(
1079 Point::new(rect.xy.x + rxlt * (1.0 - KAPPA90), rect.xy.y),
1080 Point::new(rect.xy.x, rect.xy.y + rylt * (1.0 - KAPPA90)),
1081 Point::new(rect.xy.x, rect.xy.y + rylt),
1082 ));
1083 self.append_command(Command::Close);
1084 }
1085 }
1086
1087 pub fn ellipse<P: Into<Point>>(&mut self, center: P, radius_x: f32, radius_y: f32) {
1088 let center = center.into();
1089 self.append_command(Command::MoveTo(Point::new(center.x - radius_x, center.y)));
1090 self.append_command(Command::BezierTo(
1091 Point::new(center.x - radius_x, center.y + radius_y * KAPPA90),
1092 Point::new(center.x - radius_x * KAPPA90, center.y + radius_y),
1093 Point::new(center.x, center.y + radius_y),
1094 ));
1095 self.append_command(Command::BezierTo(
1096 Point::new(center.x + radius_x * KAPPA90, center.y + radius_y),
1097 Point::new(center.x + radius_x, center.y + radius_y * KAPPA90),
1098 Point::new(center.x + radius_x, center.y),
1099 ));
1100 self.append_command(Command::BezierTo(
1101 Point::new(center.x + radius_x, center.y - radius_y * KAPPA90),
1102 Point::new(center.x + radius_x * KAPPA90, center.y - radius_y),
1103 Point::new(center.x, center.y - radius_y),
1104 ));
1105 self.append_command(Command::BezierTo(
1106 Point::new(center.x - radius_x * KAPPA90, center.y - radius_y),
1107 Point::new(center.x - radius_x, center.y - radius_y * KAPPA90),
1108 Point::new(center.x - radius_x, center.y),
1109 ));
1110 self.append_command(Command::Close);
1111 }
1112
1113 pub fn circle<P: Into<Point>>(&mut self, center: P, radius: f32) {
1114 self.ellipse(center.into(), radius, radius);
1115 }
1116
1117 pub fn fill<R: Renderer>(&mut self, renderer: &mut R) -> Result<(), NonaError> {
1118 let state = self.states.last_mut().unwrap();
1119 let mut fill_paint = state.fill.clone();
1120
1121 self.cache
1122 .flatten_paths(&self.commands, self.dist_tol, self.tess_tol);
1123 if renderer.edge_antialias() && state.shape_antialias {
1124 self.cache
1125 .expand_fill(self.fringe_width, LineJoin::Miter, 2.4, self.fringe_width);
1126 } else {
1127 self.cache
1128 .expand_fill(0.0, LineJoin::Miter, 2.4, self.fringe_width);
1129 }
1130
1131 fill_paint.inner_color.a *= state.alpha;
1132 fill_paint.outer_color.a *= state.alpha;
1133
1134 renderer.fill(
1135 &fill_paint,
1136 state.composite_operation,
1137 &state.scissor,
1138 self.fringe_width,
1139 self.cache.bounds,
1140 &self.cache.paths,
1141 )?;
1142
1143 for path in &self.cache.paths {
1144 if path.num_fill > 2 {
1145 self.fill_triangles_count += path.num_fill - 2;
1146 }
1147 if path.num_stroke > 2 {
1148 self.fill_triangles_count += path.num_stroke - 2;
1149 }
1150 self.draw_call_count += 2;
1151 }
1152
1153 Ok(())
1154 }
1155
1156 pub fn stroke<R: Renderer>(&mut self, renderer: &mut R) -> Result<(), NonaError> {
1157 let state = self.states.last_mut().unwrap();
1158 let scale = state.xform.average_scale();
1159 let mut stroke_width = (state.stroke_width * scale).clamped(0.0, 200.0);
1160 let mut stroke_paint = state.stroke.clone();
1161
1162 if stroke_width < self.fringe_width {
1163 let alpha = (stroke_width / self.fringe_width).clamped(0.0, 1.0);
1164 stroke_paint.inner_color.a *= alpha * alpha;
1165 stroke_paint.outer_color.a *= alpha * alpha;
1166 stroke_width = self.fringe_width;
1167 }
1168
1169 stroke_paint.inner_color.a *= state.alpha;
1170 stroke_paint.outer_color.a *= state.alpha;
1171
1172 self.cache
1173 .flatten_paths(&self.commands, self.dist_tol, self.tess_tol);
1174
1175 if renderer.edge_antialias() && state.shape_antialias {
1176 self.cache.expand_stroke(
1177 stroke_width * 0.5,
1178 self.fringe_width,
1179 state.line_cap,
1180 state.line_join,
1181 state.miter_limit,
1182 self.tess_tol,
1183 );
1184 } else {
1185 self.cache.expand_stroke(
1186 stroke_width * 0.5,
1187 0.0,
1188 state.line_cap,
1189 state.line_join,
1190 state.miter_limit,
1191 self.tess_tol,
1192 );
1193 }
1194
1195 renderer.stroke(
1196 &stroke_paint,
1197 state.composite_operation,
1198 &state.scissor,
1199 self.fringe_width,
1200 stroke_width,
1201 &self.cache.paths,
1202 )?;
1203
1204 for path in &self.cache.paths {
1205 self.fill_triangles_count += path.num_stroke - 2;
1206 self.draw_call_count += 1;
1207 }
1208
1209 Ok(())
1210 }
1211
1212 pub fn create_font_from_file<N: Into<String>, P: AsRef<std::path::Path>>(
1213 &mut self,
1214 name: N,
1215 path: P,
1216 ) -> Result<FontId, NonaError> {
1217 self.create_font(
1218 name,
1219 std::fs::read(path)
1220 .map_err(|err| NonaError::Texture(format!("Error loading image: {}", err)))?,
1221 )
1222 }
1223
1224 pub fn create_font<N: Into<String>, D: Into<Vec<u8>>>(
1225 &mut self,
1226 name: N,
1227 data: D,
1228 ) -> Result<FontId, NonaError> {
1229 self.fonts.add_font(name, data)
1230 }
1231
1232 pub fn find_font<N: AsRef<str>>(&self, name: N) -> Option<FontId> {
1233 self.fonts.find(name.as_ref())
1234 }
1235
1236 pub fn add_fallback_fontid(&mut self, base: FontId, fallback: FontId) {
1237 self.fonts.add_fallback(base, fallback);
1238 }
1239
1240 pub fn add_fallback_font<N1: AsRef<str>, N2: AsRef<str>>(&mut self, base: N1, fallback: N2) {
1241 if let (Some(base), Some(fallback)) = (self.find_font(base), self.find_font(fallback)) {
1242 self.fonts.add_fallback(base, fallback);
1243 }
1244 }
1245
1246 pub fn font_size(&mut self, size: f32) {
1247 self.state_mut().font_size = size;
1248 }
1249
1250 pub fn text_letter_spacing(&mut self, spacing: f32) {
1251 self.state_mut().letter_spacing = spacing;
1252 }
1253
1254 pub fn text_line_height(&mut self, line_height: f32) {
1255 self.state_mut().line_height = line_height;
1256 }
1257
1258 pub fn text_align(&mut self, align: Align) {
1259 self.state_mut().text_align = align;
1260 }
1261
1262 pub fn fontid(&mut self, id: FontId) {
1263 self.state_mut().font_id = id;
1264 }
1265
1266 pub fn font<N: AsRef<str>>(&mut self, name: N) {
1267 if let Some(id) = self.find_font(name) {
1268 self.state_mut().font_id = id;
1269 }
1270 }
1271
1272 pub fn text<S: AsRef<str>, P: Into<Point>, R: Renderer>(
1273 &mut self,
1274 renderer: &mut R,
1275 pt: P,
1276 text: S,
1277 ) -> Result<(), NonaError> {
1278 let state = self.states.last().unwrap();
1279 let scale = state.xform.font_scale() * self.device_pixel_ratio;
1280 let invscale = 1.0 / scale;
1281 let pt = pt.into();
1282
1283 self.fonts.layout_text(
1284 renderer,
1285 text.as_ref(),
1286 state.font_id,
1287 (pt.x * scale, pt.y * scale).into(),
1288 state.font_size * scale,
1289 state.text_align,
1290 state.letter_spacing * scale,
1291 true,
1292 &mut self.layout_chars,
1293 )?;
1294
1295 self.cache.vertexes.clear();
1296
1297 for lc in &self.layout_chars {
1298 let lt = Point::new(lc.bounds.min.x * invscale, lc.bounds.min.y * invscale);
1299 let rt = Point::new(lc.bounds.max.x * invscale, lc.bounds.min.y * invscale);
1300 let lb = Point::new(lc.bounds.min.x * invscale, lc.bounds.max.y * invscale);
1301 let rb = Point::new(lc.bounds.max.x * invscale, lc.bounds.max.y * invscale);
1302
1303 self.cache
1304 .vertexes
1305 .push(Vertex::new(lt.x, lt.y, lc.uv.min.x, lc.uv.min.y));
1306 self.cache
1307 .vertexes
1308 .push(Vertex::new(rb.x, rb.y, lc.uv.max.x, lc.uv.max.y));
1309 self.cache
1310 .vertexes
1311 .push(Vertex::new(rt.x, rt.y, lc.uv.max.x, lc.uv.min.y));
1312
1313 self.cache
1314 .vertexes
1315 .push(Vertex::new(lt.x, lt.y, lc.uv.min.x, lc.uv.min.y));
1316 self.cache
1317 .vertexes
1318 .push(Vertex::new(lb.x, lb.y, lc.uv.min.x, lc.uv.max.y));
1319 self.cache
1320 .vertexes
1321 .push(Vertex::new(rb.x, rb.y, lc.uv.max.x, lc.uv.max.y));
1322 }
1323
1324 let mut paint = state.fill.clone();
1325 paint.image = Some(self.fonts.img.clone());
1326 paint.inner_color.a *= state.alpha;
1327 paint.outer_color.a *= state.alpha;
1328
1329 renderer.triangles(
1330 &paint,
1331 state.composite_operation,
1332 &state.scissor,
1333 &self.cache.vertexes,
1334 )?;
1335 Ok(())
1336 }
1337
1338 pub fn text_metrics(&self) -> TextMetrics {
1339 let state = self.states.last().unwrap();
1340 let scale = state.xform.font_scale() * self.device_pixel_ratio;
1341 self.fonts
1342 .text_metrics(state.font_id, state.font_size * scale)
1343 }
1344
1345 pub fn text_size<S: AsRef<str>>(&self, text: S) -> Extent {
1346 let state = self.states.last().unwrap();
1347 let scale = state.xform.font_scale() * self.device_pixel_ratio;
1348 self.fonts.text_size(
1349 text.as_ref(),
1350 state.font_id,
1351 state.font_size * scale,
1352 state.letter_spacing * scale,
1353 )
1354 }
1355}