1use crate::basics::{is_close, is_end_poly, is_move_to, is_stop, VertexSource};
10use crate::line_aa_basics::*;
11use crate::renderer_outline_aa::OutlineAaRenderer;
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum OutlineAaJoin {
16 NoJoin,
17 Miter,
18 Round,
19 MiterAccurate,
20}
21
22#[derive(Debug, Clone, Copy)]
25pub struct LineAaVertex {
26 pub x: i32,
27 pub y: i32,
28 pub len: i32,
29}
30
31impl LineAaVertex {
32 pub fn new(x: i32, y: i32) -> Self {
33 Self { x, y, len: 0 }
34 }
35
36 pub fn calc_distance(&mut self, other: &LineAaVertex) -> bool {
40 let dx = (other.x - self.x) as f64;
41 let dy = (other.y - self.y) as f64;
42 self.len = (dx * dx + dy * dy).sqrt().round() as i32;
43 self.len > (LINE_SUBPIXEL_SCALE + LINE_SUBPIXEL_SCALE / 2)
44 }
45}
46
47struct VertexSeq {
54 data: Vec<LineAaVertex>,
55}
56
57impl VertexSeq {
58 fn new() -> Self {
59 Self { data: Vec::new() }
60 }
61
62 fn size(&self) -> usize {
63 self.data.len()
64 }
65
66 fn remove_all(&mut self) {
67 self.data.clear();
68 }
69
70 fn add(&mut self, val: LineAaVertex) {
73 if self.data.len() > 1 {
74 let n = self.data.len();
75 let last = self.data[n - 1];
76 if !self.data[n - 2].calc_distance(&last) {
77 self.data.pop();
78 }
79 }
80 self.data.push(val);
81 }
82
83 fn modify_last(&mut self, val: LineAaVertex) {
86 if !self.data.is_empty() {
87 self.data.pop();
88 }
89 self.add(val);
90 }
91
92 fn close(&mut self, closed: bool) {
95 while self.data.len() > 1 {
97 let n = self.data.len();
98 let last = self.data[n - 1];
99 if self.data[n - 2].calc_distance(&last) {
100 break;
101 }
102 let t = self.data.pop().unwrap();
103 self.modify_last(t);
104 }
105
106 if closed {
108 while self.data.len() > 1 {
109 let n = self.data.len();
110 let first = self.data[0];
111 if self.data[n - 1].calc_distance(&first) {
112 break;
113 }
114 self.data.pop();
115 }
116 }
117 }
118
119 fn get(&self, idx: usize) -> &LineAaVertex {
120 &self.data[idx]
121 }
122
123 fn get_mut(&mut self, idx: usize) -> &mut LineAaVertex {
124 &mut self.data[idx]
125 }
126}
127
128impl std::ops::Index<usize> for VertexSeq {
129 type Output = LineAaVertex;
130 fn index(&self, idx: usize) -> &LineAaVertex {
131 &self.data[idx]
132 }
133}
134
135struct DrawVars {
140 idx: usize,
141 x1: i32,
142 y1: i32,
143 x2: i32,
144 y2: i32,
145 curr: LineParameters,
146 next: LineParameters,
147 lcurr: i32,
148 lnext: i32,
149 xb1: i32,
150 yb1: i32,
151 xb2: i32,
152 yb2: i32,
153 flags: u32,
154}
155
156pub struct RasterizerOutlineAa {
166 src_vertices: VertexSeq,
167 line_join: OutlineAaJoin,
168 round_cap: bool,
169 start_x: i32,
170 start_y: i32,
171}
172
173impl RasterizerOutlineAa {
174 pub fn new() -> Self {
175 Self {
176 src_vertices: VertexSeq::new(),
177 line_join: OutlineAaJoin::NoJoin,
178 round_cap: false,
179 start_x: 0,
180 start_y: 0,
181 }
182 }
183
184 pub fn set_line_join(&mut self, join: OutlineAaJoin) {
185 self.line_join = join;
186 }
187
188 pub fn line_join(&self) -> OutlineAaJoin {
189 self.line_join
190 }
191
192 pub fn set_round_cap(&mut self, v: bool) {
193 self.round_cap = v;
194 }
195
196 pub fn round_cap(&self) -> bool {
197 self.round_cap
198 }
199
200 pub fn move_to(&mut self, x: i32, y: i32) {
201 self.start_x = x;
202 self.start_y = y;
203 self.src_vertices.modify_last(LineAaVertex::new(x, y));
204 }
205
206 pub fn line_to(&mut self, x: i32, y: i32) {
207 self.src_vertices.add(LineAaVertex::new(x, y));
208 }
209
210 pub fn move_to_d(&mut self, x: f64, y: f64) {
211 self.move_to(line_coord(x), line_coord(y));
212 }
213
214 pub fn line_to_d(&mut self, x: f64, y: f64) {
215 self.line_to(line_coord(x), line_coord(y));
216 }
217
218 fn add_vertex<R: OutlineAaRenderer>(
220 &mut self,
221 x: f64,
222 y: f64,
223 cmd: u32,
224 ren: &mut R,
225 ) {
226 if is_move_to(cmd) {
227 self.render(ren, false);
228 self.move_to_d(x, y);
229 } else if is_end_poly(cmd) {
230 self.render(ren, is_close(cmd));
231 if is_close(cmd) {
232 self.move_to(self.start_x, self.start_y);
233 }
234 } else {
235 self.line_to_d(x, y);
236 }
237 }
238
239 pub fn add_path<VS: VertexSource, R: OutlineAaRenderer>(
241 &mut self,
242 vs: &mut VS,
243 path_id: u32,
244 ren: &mut R,
245 ) {
246 vs.rewind(path_id);
247 let (mut x, mut y) = (0.0, 0.0);
248 loop {
249 let cmd = vs.vertex(&mut x, &mut y);
250 if is_stop(cmd) {
251 break;
252 }
253 self.add_vertex(x, y, cmd, ren);
254 }
255 self.render(ren, false);
257 }
258
259 fn draw<R: OutlineAaRenderer>(
264 &self,
265 dv: &mut DrawVars,
266 start: usize,
267 end: usize,
268 ren: &mut R,
269 ) {
270 for _i in start..end {
271 if self.line_join == OutlineAaJoin::Round {
272 dv.xb1 = dv.curr.x1 + (dv.curr.y2 - dv.curr.y1);
273 dv.yb1 = dv.curr.y1 - (dv.curr.x2 - dv.curr.x1);
274 dv.xb2 = dv.curr.x2 + (dv.curr.y2 - dv.curr.y1);
275 dv.yb2 = dv.curr.y2 - (dv.curr.x2 - dv.curr.x1);
276 }
277
278 match dv.flags {
279 0 => ren.line3(&dv.curr, dv.xb1, dv.yb1, dv.xb2, dv.yb2),
280 1 => ren.line2(&dv.curr, dv.xb2, dv.yb2),
281 2 => ren.line1(&dv.curr, dv.xb1, dv.yb1),
282 _ => ren.line0(&dv.curr),
283 }
284
285 if self.line_join == OutlineAaJoin::Round && (dv.flags & 2) == 0 {
286 ren.pie(
287 dv.curr.x2,
288 dv.curr.y2,
289 dv.curr.x2 + (dv.curr.y2 - dv.curr.y1),
290 dv.curr.y2 - (dv.curr.x2 - dv.curr.x1),
291 dv.curr.x2 + (dv.next.y2 - dv.next.y1),
292 dv.curr.y2 - (dv.next.x2 - dv.next.x1),
293 );
294 }
295
296 dv.x1 = dv.x2;
297 dv.y1 = dv.y2;
298 dv.lcurr = dv.lnext;
299 dv.lnext = self.src_vertices[dv.idx].len;
300
301 dv.idx += 1;
302 if dv.idx >= self.src_vertices.size() {
303 dv.idx = 0;
304 }
305
306 let v = self.src_vertices.get(dv.idx);
307 dv.x2 = v.x;
308 dv.y2 = v.y;
309
310 dv.curr = dv.next;
311 dv.next = LineParameters::new(dv.x1, dv.y1, dv.x2, dv.y2, dv.lnext);
312 dv.xb1 = dv.xb2;
313 dv.yb1 = dv.yb2;
314
315 match self.line_join {
316 OutlineAaJoin::NoJoin => {
317 dv.flags = 3;
318 }
319 OutlineAaJoin::Miter => {
320 dv.flags >>= 1;
321 dv.flags |= if dv.curr.diagonal_quadrant() == dv.next.diagonal_quadrant() {
322 2
323 } else {
324 0
325 };
326 if (dv.flags & 2) == 0 {
327 bisectrix(&dv.curr, &dv.next, &mut dv.xb2, &mut dv.yb2);
328 }
329 }
330 OutlineAaJoin::Round => {
331 dv.flags >>= 1;
332 dv.flags |= if dv.curr.diagonal_quadrant() == dv.next.diagonal_quadrant() {
333 2
334 } else {
335 0
336 };
337 }
338 OutlineAaJoin::MiterAccurate => {
339 dv.flags = 0;
340 bisectrix(&dv.curr, &dv.next, &mut dv.xb2, &mut dv.yb2);
341 }
342 }
343 }
344 }
345
346 pub fn render<R: OutlineAaRenderer>(
351 &mut self,
352 ren: &mut R,
353 close_polygon: bool,
354 ) {
355 self.src_vertices.close(close_polygon);
356
357 let saved_join = self.line_join;
362 if ren.accurate_join_only() {
363 self.line_join = OutlineAaJoin::MiterAccurate;
364 }
365
366 if close_polygon {
367 if self.src_vertices.size() >= 3 {
369 let mut dv = DrawVars {
370 idx: 2,
371 x1: 0, y1: 0, x2: 0, y2: 0,
372 curr: LineParameters::new(0, 0, 1, 0, 1), next: LineParameters::new(0, 0, 1, 0, 1),
374 lcurr: 0, lnext: 0,
375 xb1: 0, yb1: 0, xb2: 0, yb2: 0,
376 flags: 0,
377 };
378
379 let n = self.src_vertices.size();
380
381 let v_last = self.src_vertices[n - 1];
382 let x1 = v_last.x;
383 let y1 = v_last.y;
384 let lprev = v_last.len;
385
386 let v0 = self.src_vertices[0];
387 let x2 = v0.x;
388 let y2 = v0.y;
389 dv.lcurr = v0.len;
390 let prev = LineParameters::new(x1, y1, x2, y2, lprev);
391
392 let v1 = self.src_vertices[1];
393 dv.x1 = v1.x;
394 dv.y1 = v1.y;
395 dv.lnext = v1.len;
396 dv.curr = LineParameters::new(x2, y2, dv.x1, dv.y1, dv.lcurr);
397
398 let v2 = self.src_vertices[dv.idx];
399 dv.x2 = v2.x;
400 dv.y2 = v2.y;
401 dv.next = LineParameters::new(dv.x1, dv.y1, dv.x2, dv.y2, dv.lnext);
402
403 match self.line_join {
404 OutlineAaJoin::NoJoin => {
405 dv.flags = 3;
406 }
407 OutlineAaJoin::Miter | OutlineAaJoin::Round => {
408 let f1 = if prev.diagonal_quadrant() == dv.curr.diagonal_quadrant() { 1 } else { 0 };
409 let f2 = if dv.curr.diagonal_quadrant() == dv.next.diagonal_quadrant() { 2 } else { 0 };
410 dv.flags = f1 | f2;
411 }
412 OutlineAaJoin::MiterAccurate => {
413 dv.flags = 0;
414 }
415 }
416
417 if (dv.flags & 1) == 0 && self.line_join != OutlineAaJoin::Round {
418 bisectrix(&prev, &dv.curr, &mut dv.xb1, &mut dv.yb1);
419 }
420 if (dv.flags & 2) == 0 && self.line_join != OutlineAaJoin::Round {
421 bisectrix(&dv.curr, &dv.next, &mut dv.xb2, &mut dv.yb2);
422 }
423
424 self.draw(&mut dv, 0, n, ren);
425 }
426 } else {
427 let n = self.src_vertices.size();
429
430 match n {
431 0 | 1 => {} 2 => {
433 let v0 = self.src_vertices[0];
434 let x1 = v0.x;
435 let y1 = v0.y;
436 let lprev = v0.len;
437 let v1 = self.src_vertices[1];
438 let x2 = v1.x;
439 let y2 = v1.y;
440 let lp = LineParameters::new(x1, y1, x2, y2, lprev);
441
442 if self.round_cap {
443 ren.semidot(
444 cmp_dist_start,
445 x1, y1,
446 x1 + (y2 - y1), y1 - (x2 - x1),
447 );
448 }
449 ren.line3(
450 &lp,
451 x1 + (y2 - y1), y1 - (x2 - x1),
452 x2 + (y2 - y1), y2 - (x2 - x1),
453 );
454 if self.round_cap {
455 ren.semidot(
456 cmp_dist_end,
457 x2, y2,
458 x2 + (y2 - y1), y2 - (x2 - x1),
459 );
460 }
461 }
462 3 => {
463 let v0 = self.src_vertices[0];
464 let x1 = v0.x;
465 let y1 = v0.y;
466 let lprev = v0.len;
467 let v1 = self.src_vertices[1];
468 let x2 = v1.x;
469 let y2 = v1.y;
470 let lnext = v1.len;
471 let v2 = self.src_vertices[2];
472 let x3 = v2.x;
473 let y3 = v2.y;
474 let lp1 = LineParameters::new(x1, y1, x2, y2, lprev);
475 let lp2 = LineParameters::new(x2, y2, x3, y3, lnext);
476
477 if self.round_cap {
478 ren.semidot(
479 cmp_dist_start,
480 x1, y1,
481 x1 + (y2 - y1), y1 - (x2 - x1),
482 );
483 }
484
485 if self.line_join == OutlineAaJoin::Round {
486 ren.line3(
487 &lp1,
488 x1 + (y2 - y1), y1 - (x2 - x1),
489 x2 + (y2 - y1), y2 - (x2 - x1),
490 );
491 ren.pie(
492 x2, y2,
493 x2 + (y2 - y1), y2 - (x2 - x1),
494 x2 + (y3 - y2), y2 - (x3 - x2),
495 );
496 ren.line3(
497 &lp2,
498 x2 + (y3 - y2), y2 - (x3 - x2),
499 x3 + (y3 - y2), y3 - (x3 - x2),
500 );
501 } else {
502 let (mut xb1, mut yb1) = (0i32, 0i32);
503 bisectrix(&lp1, &lp2, &mut xb1, &mut yb1);
504 ren.line3(
505 &lp1,
506 x1 + (y2 - y1), y1 - (x2 - x1),
507 xb1, yb1,
508 );
509 ren.line3(
510 &lp2,
511 xb1, yb1,
512 x3 + (y3 - y2), y3 - (x3 - x2),
513 );
514 }
515
516 if self.round_cap {
517 ren.semidot(
518 cmp_dist_end,
519 x3, y3,
520 x3 + (y3 - y2), y3 - (x3 - x2),
521 );
522 }
523 }
524 _ => {
525 let mut dv = DrawVars {
527 idx: 3,
528 x1: 0, y1: 0, x2: 0, y2: 0,
529 curr: LineParameters::new(0, 0, 1, 0, 1),
530 next: LineParameters::new(0, 0, 1, 0, 1),
531 lcurr: 0, lnext: 0,
532 xb1: 0, yb1: 0, xb2: 0, yb2: 0,
533 flags: 0,
534 };
535
536 let v0 = self.src_vertices[0];
537 let x1 = v0.x;
538 let y1 = v0.y;
539 let lprev = v0.len;
540
541 let v1 = self.src_vertices[1];
542 let x2 = v1.x;
543 let y2 = v1.y;
544 dv.lcurr = v1.len;
545 let prev = LineParameters::new(x1, y1, x2, y2, lprev);
546
547 let v2 = self.src_vertices[2];
548 dv.x1 = v2.x;
549 dv.y1 = v2.y;
550 dv.lnext = v2.len;
551 dv.curr = LineParameters::new(x2, y2, dv.x1, dv.y1, dv.lcurr);
552
553 let v3 = self.src_vertices[dv.idx];
554 dv.x2 = v3.x;
555 dv.y2 = v3.y;
556 dv.next = LineParameters::new(dv.x1, dv.y1, dv.x2, dv.y2, dv.lnext);
557
558 match self.line_join {
559 OutlineAaJoin::NoJoin => {
560 dv.flags = 3;
561 }
562 OutlineAaJoin::Miter | OutlineAaJoin::Round => {
563 let f1 = if prev.diagonal_quadrant() == dv.curr.diagonal_quadrant() { 1 } else { 0 };
564 let f2 = if dv.curr.diagonal_quadrant() == dv.next.diagonal_quadrant() { 2 } else { 0 };
565 dv.flags = f1 | f2;
566 }
567 OutlineAaJoin::MiterAccurate => {
568 dv.flags = 0;
569 }
570 }
571
572 if self.round_cap {
574 ren.semidot(
575 cmp_dist_start,
576 x1, y1,
577 x1 + (y2 - y1), y1 - (x2 - x1),
578 );
579 }
580
581 if (dv.flags & 1) == 0 {
583 if self.line_join == OutlineAaJoin::Round {
584 ren.line3(
585 &prev,
586 x1 + (y2 - y1), y1 - (x2 - x1),
587 x2 + (y2 - y1), y2 - (x2 - x1),
588 );
589 ren.pie(
590 prev.x2, prev.y2,
591 x2 + (y2 - y1), y2 - (x2 - x1),
592 dv.curr.x1 + (dv.curr.y2 - dv.curr.y1),
593 dv.curr.y1 - (dv.curr.x2 - dv.curr.x1),
594 );
595 } else {
596 bisectrix(&prev, &dv.curr, &mut dv.xb1, &mut dv.yb1);
597 ren.line3(
598 &prev,
599 x1 + (y2 - y1), y1 - (x2 - x1),
600 dv.xb1, dv.yb1,
601 );
602 }
603 } else {
604 ren.line1(
605 &prev,
606 x1 + (y2 - y1), y1 - (x2 - x1),
607 );
608 }
609
610 if (dv.flags & 2) == 0 && self.line_join != OutlineAaJoin::Round {
611 bisectrix(&dv.curr, &dv.next, &mut dv.xb2, &mut dv.yb2);
612 }
613
614 self.draw(&mut dv, 1, n - 2, ren);
616
617 if (dv.flags & 1) == 0 {
619 if self.line_join == OutlineAaJoin::Round {
620 ren.line3(
621 &dv.curr,
622 dv.curr.x1 + (dv.curr.y2 - dv.curr.y1),
623 dv.curr.y1 - (dv.curr.x2 - dv.curr.x1),
624 dv.curr.x2 + (dv.curr.y2 - dv.curr.y1),
625 dv.curr.y2 - (dv.curr.x2 - dv.curr.x1),
626 );
627 } else {
628 ren.line3(
629 &dv.curr,
630 dv.xb1, dv.yb1,
631 dv.curr.x2 + (dv.curr.y2 - dv.curr.y1),
632 dv.curr.y2 - (dv.curr.x2 - dv.curr.x1),
633 );
634 }
635 } else {
636 ren.line2(
637 &dv.curr,
638 dv.curr.x2 + (dv.curr.y2 - dv.curr.y1),
639 dv.curr.y2 - (dv.curr.x2 - dv.curr.x1),
640 );
641 }
642
643 if self.round_cap {
645 ren.semidot(
646 cmp_dist_end,
647 dv.curr.x2, dv.curr.y2,
648 dv.curr.x2 + (dv.curr.y2 - dv.curr.y1),
649 dv.curr.y2 - (dv.curr.x2 - dv.curr.x1),
650 );
651 }
652 }
653 }
654 }
655 self.src_vertices.remove_all();
656 self.line_join = saved_join;
657 }
658}
659
660impl Default for RasterizerOutlineAa {
661 fn default() -> Self {
662 Self::new()
663 }
664}
665
666fn cmp_dist_start(dist: i32) -> bool {
669 dist > 0
670}
671
672fn cmp_dist_end(dist: i32) -> bool {
675 dist <= 0
676}
677
678
679#[cfg(test)]
680mod tests {
681 use super::*;
682 use crate::color::Rgba8;
683 use crate::pixfmt_rgba::PixfmtRgba32;
684 use crate::renderer_base::RendererBase;
685 use crate::renderer_outline_aa::{LineProfileAa, RendererOutlineAa};
686 use crate::rendering_buffer::RowAccessor;
687
688 fn make_buffer(w: u32, h: u32) -> (Vec<u8>, RowAccessor) {
689 let stride = (w * 4) as i32;
690 let buf = vec![0u8; (h * w * 4) as usize];
691 let mut ra = RowAccessor::new();
692 unsafe {
693 ra.attach(buf.as_ptr() as *mut u8, w, h, stride);
694 }
695 (buf, ra)
696 }
697
698 #[test]
699 fn test_rasterizer_creation() {
700 let ras = RasterizerOutlineAa::new();
701 assert_eq!(ras.line_join(), OutlineAaJoin::NoJoin);
702 assert!(!ras.round_cap());
703 }
704
705 fn scan_for_color(ren_aa: &RendererOutlineAa<PixfmtRgba32>, cx: i32, cy: i32, radius: i32, channel: &str) -> bool {
706 for y in (cy - radius)..=(cy + radius) {
707 for x in (cx - radius)..=(cx + radius) {
708 if x < 0 || y < 0 { continue; }
709 let p = ren_aa.ren().pixel(x, y);
710 match channel {
711 "r" => { if p.r > 0 { return true; } }
712 "g" => { if p.g > 0 { return true; } }
713 "b" => { if p.b > 0 { return true; } }
714 _ => {}
715 }
716 }
717 }
718 false
719 }
720
721 #[test]
722 fn test_rasterizer_two_points() {
723 let (_buf, mut ra) = make_buffer(100, 100);
724 let pixf = PixfmtRgba32::new(&mut ra);
725 let mut ren = RendererBase::new(pixf);
726 let prof = LineProfileAa::with_width(2.0);
727 let mut ren_aa = RendererOutlineAa::new(&mut ren, &prof);
728 ren_aa.set_color(Rgba8::new(255, 0, 0, 255));
729
730 let mut ras = RasterizerOutlineAa::new();
731 ras.move_to_d(10.0, 50.0);
732 ras.line_to_d(90.0, 50.0);
733 ras.render(&mut ren_aa, false);
734
735 assert!(scan_for_color(&ren_aa, 50, 50, 2, "r"), "Expected red pixels near (50,50)");
736 }
737
738 #[test]
739 fn test_rasterizer_polyline() {
740 let (_buf, mut ra) = make_buffer(100, 100);
741 let pixf = PixfmtRgba32::new(&mut ra);
742 let mut ren = RendererBase::new(pixf);
743 let prof = LineProfileAa::with_width(1.5);
744 let mut ren_aa = RendererOutlineAa::new(&mut ren, &prof);
745 ren_aa.set_color(Rgba8::new(0, 255, 0, 255));
746
747 let mut ras = RasterizerOutlineAa::new();
748 ras.set_line_join(OutlineAaJoin::Miter);
749 ras.move_to_d(10.0, 10.0);
750 ras.line_to_d(50.0, 50.0);
751 ras.line_to_d(90.0, 10.0);
752 ras.render(&mut ren_aa, false);
753
754 assert!(scan_for_color(&ren_aa, 50, 50, 2, "g"), "Expected green pixels near (50,50)");
755 }
756
757 #[test]
758 fn test_rasterizer_closed_polygon() {
759 let (_buf, mut ra) = make_buffer(100, 100);
760 let pixf = PixfmtRgba32::new(&mut ra);
761 let mut ren = RendererBase::new(pixf);
762 let prof = LineProfileAa::with_width(1.0);
763 let mut ren_aa = RendererOutlineAa::new(&mut ren, &prof);
764 ren_aa.set_color(Rgba8::new(0, 0, 255, 255));
765
766 let mut ras = RasterizerOutlineAa::new();
767 ras.move_to_d(20.0, 20.0);
768 ras.line_to_d(80.0, 20.0);
769 ras.line_to_d(80.0, 80.0);
770 ras.line_to_d(20.0, 80.0);
771 ras.render(&mut ren_aa, true);
772
773 assert!(scan_for_color(&ren_aa, 50, 20, 2, "b"), "Expected blue pixels near (50,20)");
774 }
775}