1use crate::basics::{iround, RectI};
10use crate::color::{Rgba, Rgba8};
11use crate::dda_line::Dda2LineInterpolator;
12use crate::line_aa_basics::*;
13use crate::pattern_filters_rgba::PatternFilter;
14use crate::pixfmt_rgba::PixelFormat;
15use crate::renderer_base::RendererBase;
16use crate::renderer_outline_aa::OutlineAaRenderer;
17
18pub trait ImagePatternSource {
27 fn width(&self) -> f64;
28 fn height(&self) -> f64;
29 fn pixel(&self, x: i32, y: i32) -> Rgba8;
30
31 fn pixel_rgba(&self, x: i32, y: i32) -> Rgba {
44 rgba8_to_rgba(self.pixel(x, y))
45 }
46}
47
48pub struct LineImageScale<'a, S: ImagePatternSource> {
57 source: &'a S,
58 height: f64,
59 scale: f64,
60 scale_inv: f64,
61}
62
63#[inline]
65fn rgba8_to_rgba(c: Rgba8) -> Rgba {
66 Rgba::new(
67 c.r as f64 / 255.0,
68 c.g as f64 / 255.0,
69 c.b as f64 / 255.0,
70 c.a as f64 / 255.0,
71 )
72}
73
74#[inline]
77fn rgba_to_rgba8(c: &Rgba) -> Rgba8 {
78 #[inline]
79 fn clamp_u8(v: f64) -> u32 {
80 let i = (v * 255.0 + 0.5) as i32;
81 i.clamp(0, 255) as u32
82 }
83 Rgba8::new(clamp_u8(c.r), clamp_u8(c.g), clamp_u8(c.b), clamp_u8(c.a))
84}
85
86impl<'a, S: ImagePatternSource> LineImageScale<'a, S> {
87 pub fn new(source: &'a S, height: f64) -> Self {
88 let sh = source.height();
89 Self {
90 source,
91 height,
92 scale: sh / height,
93 scale_inv: height / sh,
94 }
95 }
96}
97
98impl<'a, S: ImagePatternSource> ImagePatternSource for LineImageScale<'a, S> {
99 fn width(&self) -> f64 {
100 self.source.width()
101 }
102
103 fn height(&self) -> f64 {
104 self.height
105 }
106
107 fn pixel(&self, x: i32, y: i32) -> Rgba8 {
114 let h = self.source.height() as i32 - 1;
115
116 if self.scale < 1.0 {
117 let src_y = (y as f64 + 0.5) * self.scale - 0.5;
119 let y1 = src_y.floor() as i32;
120 let y2 = y1 + 1;
121 let pix1 = if y1 < 0 {
122 Rgba::no_color()
123 } else {
124 self.source.pixel_rgba(x, y1)
125 };
126 let pix2 = if y2 > h {
127 Rgba::no_color()
128 } else {
129 self.source.pixel_rgba(x, y2)
130 };
131 let k = src_y - y1 as f64;
132 rgba_to_rgba8(&pix1.gradient(&pix2, k))
133 } else {
134 let src_y1 = (y as f64 + 0.5) * self.scale - 0.5;
136 let src_y2 = src_y1 + self.scale;
137 let mut y1 = src_y1.floor() as i32;
138 let y2 = src_y2.floor() as i32;
139
140 let mut c = Rgba::no_color();
141
142 if y1 >= 0 {
144 let weight = (y1 + 1) as f64 - src_y1;
145 let p = self.source.pixel_rgba(x, y1);
146 c += p * weight;
147 }
148
149 y1 += 1;
151 while y1 < y2 {
152 if y1 <= h {
153 c += self.source.pixel_rgba(x, y1);
154 }
155 y1 += 1;
156 }
157
158 if y2 <= h {
160 let weight = src_y2 - y2 as f64;
161 let p = self.source.pixel_rgba(x, y2);
162 c += p * weight;
163 }
164
165 c *= self.scale_inv;
166 rgba_to_rgba8(&c)
167 }
168 }
169}
170
171#[inline]
177fn uceil(v: f64) -> u32 {
178 v.ceil() as u32
179}
180
181#[inline]
183fn uround(v: f64) -> i32 {
184 (v + 0.5) as i32
185}
186
187pub struct LineImagePattern<F: PatternFilter> {
193 _phantom: std::marker::PhantomData<F>,
194 buf: Vec<Vec<Rgba8>>,
196 dilation: u32,
197 dilation_hr: i32,
198 width: u32,
199 height: u32,
200 width_hr: i32,
201 half_height_hr: i32,
202 offset_y_hr: i32,
203}
204
205impl<F: PatternFilter> LineImagePattern<F> {
206 pub fn new() -> Self {
208 let dilation = F::dilation() + 1;
209 Self {
210 _phantom: std::marker::PhantomData,
211 buf: Vec::new(),
212 dilation,
213 dilation_hr: (dilation as i32) << LINE_SUBPIXEL_SHIFT,
214 width: 0,
215 height: 0,
216 width_hr: 0,
217 half_height_hr: 0,
218 offset_y_hr: 0,
219 }
220 }
221
222 pub fn with_source<S: ImagePatternSource>(src: &S) -> Self {
224 let mut p = Self::new();
225 p.create(src);
226 p
227 }
228
229 pub fn create<S: ImagePatternSource>(&mut self, src: &S) {
233 self.height = uceil(src.height());
234 self.width = uceil(src.width());
235 self.width_hr = uround(src.width() * LINE_SUBPIXEL_SCALE as f64);
236 self.half_height_hr = uround(src.height() * LINE_SUBPIXEL_SCALE as f64 / 2.0);
237 self.offset_y_hr =
238 self.dilation_hr + self.half_height_hr - LINE_SUBPIXEL_SCALE / 2;
239 self.half_height_hr += LINE_SUBPIXEL_SCALE / 2;
240
241 let total_w = (self.width + self.dilation * 2) as usize;
242 let total_h = (self.height + self.dilation * 2) as usize;
243
244 self.buf = vec![vec![Rgba8::new(0, 0, 0, 0); total_w]; total_h];
246
247 for y in 0..self.height as usize {
249 let row = &mut self.buf[y + self.dilation as usize];
250 for x in 0..self.width as usize {
251 row[x + self.dilation as usize] = src.pixel(x as i32, y as i32);
252 }
253 }
254
255 let no_color = Rgba8::new(0, 0, 0, 0);
257 for dy in 0..self.dilation as usize {
258 let row_bot = &mut self.buf[self.dilation as usize + self.height as usize + dy];
260 for x in 0..self.width as usize {
261 row_bot[x + self.dilation as usize] = no_color;
262 }
263 let row_top = &mut self.buf[self.dilation as usize - dy - 1];
265 for x in 0..self.width as usize {
266 row_top[x + self.dilation as usize] = no_color;
267 }
268 }
269
270 for y in 0..total_h {
273 for dx in 0..self.dilation as usize {
274 let src_val = self.buf[y][self.dilation as usize + dx];
276 self.buf[y][self.dilation as usize + self.width as usize + dx] = src_val;
277
278 let src_val = self.buf[y]
280 [self.dilation as usize + self.width as usize - 1 - dx];
281 self.buf[y][self.dilation as usize - 1 - dx] = src_val;
282 }
283 }
284 }
285
286 pub fn pattern_width(&self) -> i32 {
288 self.width_hr
289 }
290
291 pub fn line_width(&self) -> i32 {
293 self.half_height_hr
294 }
295
296 pub fn width(&self) -> f64 {
298 self.height as f64
299 }
300
301 #[inline]
305 pub fn pixel(&self, p: &mut Rgba8, x: i32, y: i32) {
306 F::pixel_high_res(
307 &self.buf,
308 p,
309 x % self.width_hr + self.dilation_hr,
310 y + self.offset_y_hr,
311 );
312 }
313}
314
315pub struct LineImagePatternPow2<F: PatternFilter> {
324 base: LineImagePattern<F>,
325 mask: u32,
326}
327
328impl<F: PatternFilter> LineImagePatternPow2<F> {
329 pub fn new() -> Self {
330 Self {
331 base: LineImagePattern::new(),
332 mask: LINE_SUBPIXEL_MASK as u32,
333 }
334 }
335
336 pub fn with_source<S: ImagePatternSource>(src: &S) -> Self {
337 let mut p = Self::new();
338 p.create(src);
339 p
340 }
341
342 pub fn create<S: ImagePatternSource>(&mut self, src: &S) {
343 self.base.create(src);
344 self.mask = 1;
345 while self.mask < self.base.width {
346 self.mask <<= 1;
347 self.mask |= 1;
348 }
349 self.mask <<= LINE_SUBPIXEL_SHIFT as u32 - 1;
350 self.mask |= LINE_SUBPIXEL_MASK as u32;
351 self.base.width_hr = self.mask as i32 + 1;
352 }
353
354 pub fn pattern_width(&self) -> i32 {
355 self.base.width_hr
356 }
357
358 pub fn line_width(&self) -> i32 {
359 self.base.half_height_hr
360 }
361
362 pub fn width(&self) -> f64 {
363 self.base.height as f64
364 }
365
366 #[inline]
367 pub fn pixel(&self, p: &mut Rgba8, x: i32, y: i32) {
368 F::pixel_high_res(
369 &self.base.buf,
370 p,
371 (x & self.mask as i32) + self.base.dilation_hr,
372 y + self.base.offset_y_hr,
373 );
374 }
375}
376
377pub trait ImageLinePattern {
386 fn pattern_width(&self) -> i32;
388 fn line_width(&self) -> i32;
390 fn width(&self) -> f64;
392 fn pixel(&self, p: &mut Rgba8, x: i32, y: i32);
394}
395
396impl<F: PatternFilter> ImageLinePattern for LineImagePattern<F> {
397 fn pattern_width(&self) -> i32 { self.pattern_width() }
398 fn line_width(&self) -> i32 { self.line_width() }
399 fn width(&self) -> f64 { self.width() }
400 fn pixel(&self, p: &mut Rgba8, x: i32, y: i32) { self.pixel(p, x, y) }
401}
402
403impl<F: PatternFilter> ImageLinePattern for LineImagePatternPow2<F> {
404 fn pattern_width(&self) -> i32 { self.pattern_width() }
405 fn line_width(&self) -> i32 { self.line_width() }
406 fn width(&self) -> f64 { self.width() }
407 fn pixel(&self, p: &mut Rgba8, x: i32, y: i32) { self.pixel(p, x, y) }
408}
409
410pub struct DistanceInterpolator4 {
419 dx: i32,
420 dy: i32,
421 dx_start: i32,
422 dy_start: i32,
423 dx_pict: i32,
424 dy_pict: i32,
425 dx_end: i32,
426 dy_end: i32,
427 dist: i32,
428 dist_start: i32,
429 dist_pict: i32,
430 dist_end: i32,
431 len: i32,
432}
433
434impl DistanceInterpolator4 {
435 #[allow(clippy::too_many_arguments)]
436 pub fn new(
437 x1: i32, y1: i32, x2: i32, y2: i32,
438 sx: i32, sy: i32, ex: i32, ey: i32,
439 len: i32, scale: f64, x: i32, y: i32,
440 ) -> Self {
441 let mut dx = x2 - x1;
442 let mut dy = y2 - y1;
443 let mut dx_start = line_mr(sx) - line_mr(x1);
444 let mut dy_start = line_mr(sy) - line_mr(y1);
445 let mut dx_end = line_mr(ex) - line_mr(x2);
446 let mut dy_end = line_mr(ey) - line_mr(y2);
447
448 let dist = iround(
449 (x + LINE_SUBPIXEL_SCALE / 2 - x2) as f64 * dy as f64
450 - (y + LINE_SUBPIXEL_SCALE / 2 - y2) as f64 * dx as f64,
451 );
452
453 let dist_start = (line_mr(x + LINE_SUBPIXEL_SCALE / 2) - line_mr(sx)) * dy_start
454 - (line_mr(y + LINE_SUBPIXEL_SCALE / 2) - line_mr(sy)) * dx_start;
455
456 let dist_end = (line_mr(x + LINE_SUBPIXEL_SCALE / 2) - line_mr(ex)) * dy_end
457 - (line_mr(y + LINE_SUBPIXEL_SCALE / 2) - line_mr(ey)) * dx_end;
458
459 let ilen = uround(len as f64 / scale);
460
461 let d = len as f64 * scale;
462 let dx_f = iround(((x2 - x1) << LINE_SUBPIXEL_SHIFT) as f64 / d);
463 let dy_f = iround(((y2 - y1) << LINE_SUBPIXEL_SHIFT) as f64 / d);
464 let dx_pict = -dy_f;
465 let dy_pict = dx_f;
466 let dist_pict = ((x + LINE_SUBPIXEL_SCALE / 2 - (x1 - dy_f)) as i64
467 * dy_pict as i64
468 - (y + LINE_SUBPIXEL_SCALE / 2 - (y1 + dx_f)) as i64
469 * dx_pict as i64)
470 >> LINE_SUBPIXEL_SHIFT;
471
472 dx <<= LINE_SUBPIXEL_SHIFT;
473 dy <<= LINE_SUBPIXEL_SHIFT;
474 dx_start <<= LINE_MR_SUBPIXEL_SHIFT;
475 dy_start <<= LINE_MR_SUBPIXEL_SHIFT;
476 dx_end <<= LINE_MR_SUBPIXEL_SHIFT;
477 dy_end <<= LINE_MR_SUBPIXEL_SHIFT;
478
479 Self {
480 dx, dy, dx_start, dy_start, dx_pict, dy_pict, dx_end, dy_end,
481 dist, dist_start, dist_pict: dist_pict as i32, dist_end,
482 len: ilen,
483 }
484 }
485
486 #[inline]
487 pub fn inc_x(&mut self, dy: i32) {
488 self.dist += self.dy;
489 self.dist_start += self.dy_start;
490 self.dist_pict += self.dy_pict;
491 self.dist_end += self.dy_end;
492 if dy > 0 {
493 self.dist -= self.dx;
494 self.dist_start -= self.dx_start;
495 self.dist_pict -= self.dx_pict;
496 self.dist_end -= self.dx_end;
497 }
498 if dy < 0 {
499 self.dist += self.dx;
500 self.dist_start += self.dx_start;
501 self.dist_pict += self.dx_pict;
502 self.dist_end += self.dx_end;
503 }
504 }
505
506 #[inline]
507 pub fn dec_x(&mut self, dy: i32) {
508 self.dist -= self.dy;
509 self.dist_start -= self.dy_start;
510 self.dist_pict -= self.dy_pict;
511 self.dist_end -= self.dy_end;
512 if dy > 0 {
513 self.dist -= self.dx;
514 self.dist_start -= self.dx_start;
515 self.dist_pict -= self.dx_pict;
516 self.dist_end -= self.dx_end;
517 }
518 if dy < 0 {
519 self.dist += self.dx;
520 self.dist_start += self.dx_start;
521 self.dist_pict += self.dx_pict;
522 self.dist_end += self.dx_end;
523 }
524 }
525
526 #[inline]
527 pub fn inc_y(&mut self, dx: i32) {
528 self.dist -= self.dx;
529 self.dist_start -= self.dx_start;
530 self.dist_pict -= self.dx_pict;
531 self.dist_end -= self.dx_end;
532 if dx > 0 {
533 self.dist += self.dy;
534 self.dist_start += self.dy_start;
535 self.dist_pict += self.dy_pict;
536 self.dist_end += self.dy_end;
537 }
538 if dx < 0 {
539 self.dist -= self.dy;
540 self.dist_start -= self.dy_start;
541 self.dist_pict -= self.dy_pict;
542 self.dist_end -= self.dy_end;
543 }
544 }
545
546 #[inline]
547 pub fn dec_y(&mut self, dx: i32) {
548 self.dist += self.dx;
549 self.dist_start += self.dx_start;
550 self.dist_pict += self.dx_pict;
551 self.dist_end += self.dx_end;
552 if dx > 0 {
553 self.dist += self.dy;
554 self.dist_start += self.dy_start;
555 self.dist_pict += self.dy_pict;
556 self.dist_end += self.dy_end;
557 }
558 if dx < 0 {
559 self.dist -= self.dy;
560 self.dist_start -= self.dy_start;
561 self.dist_pict -= self.dy_pict;
562 self.dist_end -= self.dy_end;
563 }
564 }
565
566 #[inline] pub fn dist(&self) -> i32 { self.dist }
567 #[inline] pub fn dist_start(&self) -> i32 { self.dist_start }
568 #[inline] pub fn dist_pict(&self) -> i32 { self.dist_pict }
569 #[inline] pub fn dist_end(&self) -> i32 { self.dist_end }
570 #[inline] pub fn dx_start(&self) -> i32 { self.dx_start }
571 #[inline] pub fn dy_start(&self) -> i32 { self.dy_start }
572 #[inline] pub fn dx_pict(&self) -> i32 { self.dx_pict }
573 #[inline] pub fn dy_pict(&self) -> i32 { self.dy_pict }
574 #[inline] pub fn dx_end(&self) -> i32 { self.dx_end }
575 #[inline] pub fn dy_end(&self) -> i32 { self.dy_end }
576 #[inline] pub fn len(&self) -> i32 { self.len }
577}
578
579pub struct RendererOutlineImage<'a, PF: PixelFormat, P: ImageLinePattern> {
592 ren: &'a mut RendererBase<PF>,
593 pattern: &'a P,
594 start: i32,
595 scale_x: f64,
596 clip_box: RectI,
597 clipping: bool,
598}
599
600impl<'a, PF: PixelFormat, P: ImageLinePattern> RendererOutlineImage<'a, PF, P>
601where
602 PF::ColorType: Default + Clone + From<Rgba8>,
603{
604 pub fn new(ren: &'a mut RendererBase<PF>, pattern: &'a P) -> Self {
605 Self {
606 ren,
607 pattern,
608 start: 0,
609 scale_x: 1.0,
610 clip_box: RectI::new(0, 0, 0, 0),
611 clipping: false,
612 }
613 }
614
615 pub fn reset_clipping(&mut self) {
616 self.clipping = false;
617 }
618
619 pub fn set_clip_box(&mut self, x1: f64, y1: f64, x2: f64, y2: f64) {
620 self.clip_box = RectI::new(
621 line_coord_sat(x1),
622 line_coord_sat(y1),
623 line_coord_sat(x2),
624 line_coord_sat(y2),
625 );
626 self.clipping = true;
627 }
628
629 pub fn set_scale_x(&mut self, s: f64) {
630 self.scale_x = s;
631 }
632
633 pub fn scale_x(&self) -> f64 {
634 self.scale_x
635 }
636
637 pub fn set_start_x(&mut self, s: f64) {
638 self.start = iround(s * LINE_SUBPIXEL_SCALE as f64);
639 }
640
641 pub fn start_x(&self) -> f64 {
642 self.start as f64 / LINE_SUBPIXEL_SCALE as f64
643 }
644
645 pub fn subpixel_width(&self) -> i32 {
646 self.pattern.line_width()
647 }
648
649 pub fn pattern_width(&self) -> i32 {
650 self.pattern.pattern_width()
651 }
652
653 pub fn width(&self) -> f64 {
654 self.subpixel_width() as f64 / LINE_SUBPIXEL_SCALE as f64
655 }
656
657 fn line3_no_clip(
661 &mut self,
662 lp: &LineParameters,
663 sx: i32, sy: i32,
664 ex: i32, ey: i32,
665 ) {
666 if lp.len > LINE_MAX_LENGTH {
667 let (lp1, lp2) = lp.divide();
668 let mx = lp1.x2 + (lp1.y2 - lp1.y1);
669 let my = lp1.y2 - (lp1.x2 - lp1.x1);
670 self.line3_no_clip(
671 &lp1,
672 (lp.x1 + sx) >> 1, (lp.y1 + sy) >> 1,
673 mx, my,
674 );
675 self.line3_no_clip(
676 &lp2,
677 mx, my,
678 (lp.x2 + ex) >> 1, (lp.y2 + ey) >> 1,
679 );
680 return;
681 }
682
683 let mut sx = sx;
684 let mut sy = sy;
685 let mut ex = ex;
686 let mut ey = ey;
687 fix_degenerate_bisectrix_start(lp, &mut sx, &mut sy);
688 fix_degenerate_bisectrix_end(lp, &mut ex, &mut ey);
689
690 self.render_line_image(lp, sx, sy, ex, ey);
693
694 self.start += uround(lp.len as f64 / self.scale_x);
695 }
696
697 #[allow(clippy::too_many_arguments)]
702 fn render_line_image(
703 &mut self,
704 lp: &LineParameters,
705 sx: i32, sy: i32,
706 ex: i32, ey: i32,
707 ) {
708 const MAX_HW: usize = 64;
709 const DIST_SIZE: usize = MAX_HW + 1;
710 const COLOR_SIZE: usize = MAX_HW * 2 + 4;
711
712 let li_init = if lp.vertical {
713 Dda2LineInterpolator::new_relative(
714 line_dbl_hr(lp.x2 - lp.x1),
715 (lp.y2 - lp.y1).abs(),
716 )
717 } else {
718 Dda2LineInterpolator::new_relative(
719 line_dbl_hr(lp.y2 - lp.y1),
720 (lp.x2 - lp.x1).abs() + 1,
721 )
722 };
723
724 let di_init = DistanceInterpolator4::new(
725 lp.x1, lp.y1, lp.x2, lp.y2,
726 sx, sy, ex, ey,
727 lp.len, self.scale_x,
728 lp.x1 & !LINE_SUBPIXEL_MASK,
729 lp.y1 & !LINE_SUBPIXEL_MASK,
730 );
731
732 let ix = lp.x1 >> LINE_SUBPIXEL_SHIFT;
733 let iy = lp.y1 >> LINE_SUBPIXEL_SHIFT;
734
735 let count = if lp.vertical {
736 ((lp.y2 >> LINE_SUBPIXEL_SHIFT) - iy).abs()
737 } else {
738 ((lp.x2 >> LINE_SUBPIXEL_SHIFT) - ix).abs()
739 };
740
741 let width = self.pattern.line_width();
742 let max_extent = (width + LINE_SUBPIXEL_SCALE) >> LINE_SUBPIXEL_SHIFT;
743 let start = self.start + (max_extent + 2) * self.pattern.pattern_width();
744
745 let mut dist_pos = [0i32; DIST_SIZE];
747 {
748 let mut dd = Dda2LineInterpolator::new_forward(
749 0,
750 if lp.vertical { lp.dy << LINE_SUBPIXEL_SHIFT } else { lp.dx << LINE_SUBPIXEL_SHIFT },
751 lp.len,
752 );
753 let stop = width + LINE_SUBPIXEL_SCALE * 2;
754 let mut i = 0;
755 while i < MAX_HW {
756 dist_pos[i] = dd.y();
757 if dist_pos[i] >= stop { break; }
758 dd.inc();
759 i += 1;
760 }
761 if i <= MAX_HW {
762 dist_pos[i] = 0x7FFF_0000;
763 }
764 }
765
766 let mut li = li_init;
767 let mut di = di_init;
768 let mut x = ix;
769 let mut y = iy;
770 let mut old_x = ix;
771 let mut old_y = iy;
772 let mut step = 0i32;
773
774 let mut npix = 1i32;
776 if lp.vertical {
777 loop {
778 li.dec();
779 y -= lp.inc;
780 x = (lp.x1 + li.y()) >> LINE_SUBPIXEL_SHIFT;
781 if lp.inc > 0 { di.dec_y(x - old_x); }
782 else { di.inc_y(x - old_x); }
783 old_x = x;
784
785 let mut d1 = di.dist_start();
786 let mut d2 = d1;
787 let mut dx = 0usize;
788 if d1 < 0 { npix += 1; }
789 loop {
790 d1 += di.dy_start();
791 d2 -= di.dy_start();
792 if d1 < 0 { npix += 1; }
793 if d2 < 0 { npix += 1; }
794 dx += 1;
795 if dist_pos[dx] > width { break; }
796 }
797 if npix == 0 { break; }
798 npix = 0;
799 step -= 1;
800 if step < -max_extent { break; }
801 }
802 } else {
803 loop {
804 li.dec();
805 x -= lp.inc;
806 y = (lp.y1 + li.y()) >> LINE_SUBPIXEL_SHIFT;
807 if lp.inc > 0 { di.dec_x(y - old_y); }
808 else { di.inc_x(y - old_y); }
809 old_y = y;
810
811 let mut d1 = di.dist_start();
812 let mut d2 = d1;
813 let mut dy = 0usize;
814 if d1 < 0 { npix += 1; }
815 loop {
816 d1 -= di.dx_start();
817 d2 += di.dx_start();
818 if d1 < 0 { npix += 1; }
819 if d2 < 0 { npix += 1; }
820 dy += 1;
821 if dist_pos[dy] > width { break; }
822 }
823 if npix == 0 { break; }
824 npix = 0;
825 step -= 1;
826 if step < -max_extent { break; }
827 }
828 }
829
830 li.adjust_forward();
831
832 step -= max_extent;
833
834 let mut colors = [Rgba8::new(0, 0, 0, 0); COLOR_SIZE];
836
837 if lp.vertical {
838 loop {
839 li.inc();
841 y += lp.inc;
842 x = (lp.x1 + li.y()) >> LINE_SUBPIXEL_SHIFT;
843 if lp.inc > 0 { di.inc_y(x - old_x); }
844 else { di.dec_y(x - old_x); }
845 old_x = x;
846
847 let s1 = di.dist() / lp.len;
848 let s2 = -s1;
849 let s1_adj = if lp.inc > 0 { -s1 } else { s1 };
850
851 let mut dist_start = di.dist_start();
852 let mut dist_pict = di.dist_pict() + start;
853 let mut dist_end = di.dist_end();
854
855 let center = MAX_HW + 2;
856 let mut p0 = center;
857 let mut p1 = center;
858
859 let mut n = 0;
860 colors[p1] = Rgba8::new(0, 0, 0, 0);
861 if dist_end > 0 {
862 if dist_start <= 0 {
863 self.pattern.pixel(&mut colors[p1], dist_pict, s2);
864 }
865 n += 1;
866 }
867 p1 += 1;
868
869 let mut dx = 1usize;
870 while dx < DIST_SIZE && dist_pos[dx] - s1_adj <= width {
871 dist_start += di.dy_start();
872 dist_pict += di.dy_pict();
873 dist_end += di.dy_end();
874 colors[p1] = Rgba8::new(0, 0, 0, 0);
875 if dist_end > 0 && dist_start <= 0 {
876 let mut d = dist_pos[dx];
877 if lp.inc > 0 { d = -d; }
878 self.pattern.pixel(&mut colors[p1], dist_pict, s2 + d);
879 n += 1;
880 }
881 p1 += 1;
882 dx += 1;
883 }
884
885 dx = 1;
886 dist_start = di.dist_start();
887 dist_pict = di.dist_pict() + start;
888 dist_end = di.dist_end();
889 while dx < DIST_SIZE && dist_pos[dx] + s1_adj <= width {
890 dist_start -= di.dy_start();
891 dist_pict -= di.dy_pict();
892 dist_end -= di.dy_end();
893 p0 -= 1;
894 colors[p0] = Rgba8::new(0, 0, 0, 0);
895 if dist_end > 0 && dist_start <= 0 {
896 let mut d = dist_pos[dx];
897 if lp.inc > 0 { d = -d; }
898 self.pattern.pixel(&mut colors[p0], dist_pict, s2 - d);
899 n += 1;
900 }
901 dx += 1;
902 }
903
904 let len = p1 - p0;
906 if len > 0 {
907 let cvec: Vec<PF::ColorType> = colors[p0..p1]
908 .iter()
909 .map(|c| PF::ColorType::from(*c))
910 .collect();
911 self.ren.blend_color_hspan(
912 x - dx as i32 + 1, y, len as i32,
913 &cvec, &[], 255,
914 );
915 }
916
917 step += 1;
918 if n == 0 || step >= count {
919 break;
920 }
921 }
922 } else {
923 loop {
924 li.inc();
926 x += lp.inc;
927 y = (lp.y1 + li.y()) >> LINE_SUBPIXEL_SHIFT;
928 if lp.inc > 0 { di.inc_x(y - old_y); }
929 else { di.dec_x(y - old_y); }
930 old_y = y;
931
932 let s1 = di.dist() / lp.len;
933 let s2 = -s1;
934 let s1_adj = if lp.inc < 0 { -s1 } else { s1 };
935
936 let mut dist_start = di.dist_start();
937 let mut dist_pict = di.dist_pict() + start;
938 let mut dist_end = di.dist_end();
939
940 let center = MAX_HW + 2;
941 let mut p0 = center;
942 let mut p1 = center;
943
944 let mut n = 0;
945 colors[p1] = Rgba8::new(0, 0, 0, 0);
946 if dist_end > 0 {
947 if dist_start <= 0 {
948 self.pattern.pixel(&mut colors[p1], dist_pict, s2);
949 }
950 n += 1;
951 }
952 p1 += 1;
953
954 let mut dy = 1usize;
955 while dy < DIST_SIZE && dist_pos[dy] - s1_adj <= width {
956 dist_start -= di.dx_start();
957 dist_pict -= di.dx_pict();
958 dist_end -= di.dx_end();
959 colors[p1] = Rgba8::new(0, 0, 0, 0);
960 if dist_end > 0 && dist_start <= 0 {
961 let mut d = dist_pos[dy];
962 if lp.inc > 0 { d = -d; }
963 self.pattern.pixel(&mut colors[p1], dist_pict, s2 - d);
964 n += 1;
965 }
966 p1 += 1;
967 dy += 1;
968 }
969
970 dy = 1;
971 dist_start = di.dist_start();
972 dist_pict = di.dist_pict() + start;
973 dist_end = di.dist_end();
974 while dy < DIST_SIZE && dist_pos[dy] + s1_adj <= width {
975 dist_start += di.dx_start();
976 dist_pict += di.dx_pict();
977 dist_end += di.dx_end();
978 p0 -= 1;
979 colors[p0] = Rgba8::new(0, 0, 0, 0);
980 if dist_end > 0 && dist_start <= 0 {
981 let mut d = dist_pos[dy];
982 if lp.inc > 0 { d = -d; }
983 self.pattern.pixel(&mut colors[p0], dist_pict, s2 + d);
984 n += 1;
985 }
986 dy += 1;
987 }
988
989 let len = p1 - p0;
991 if len > 0 {
992 let cvec: Vec<PF::ColorType> = colors[p0..p1]
993 .iter()
994 .map(|c| PF::ColorType::from(*c))
995 .collect();
996 self.ren.blend_color_vspan(
997 x, y - dy as i32 + 1, len as i32,
998 &cvec, &[], 255,
999 );
1000 }
1001
1002 step += 1;
1003 if n == 0 || step >= count {
1004 break;
1005 }
1006 }
1007 }
1008 }
1009}
1010
1011impl<'a, PF: PixelFormat, P: ImageLinePattern> OutlineAaRenderer
1012 for RendererOutlineImage<'a, PF, P>
1013where
1014 PF::ColorType: Default + Clone + From<Rgba8>,
1015{
1016 fn accurate_join_only(&self) -> bool {
1017 true
1018 }
1019
1020 fn line0(&mut self, _lp: &LineParameters) {}
1022 fn line1(&mut self, _lp: &LineParameters, _sx: i32, _sy: i32) {}
1023 fn line2(&mut self, _lp: &LineParameters, _ex: i32, _ey: i32) {}
1024
1025 fn line3(&mut self, lp: &LineParameters, sx: i32, sy: i32, ex: i32, ey: i32) {
1026 if self.clipping {
1027 let mut x1 = lp.x1;
1028 let mut y1 = lp.y1;
1029 let mut x2 = lp.x2;
1030 let mut y2 = lp.y2;
1031 let flags = clip_line_segment(&mut x1, &mut y1, &mut x2, &mut y2, &self.clip_box);
1032 let start = self.start;
1033 if (flags & 4) == 0 {
1034 if flags != 0 {
1035 let lp2 = LineParameters::new(
1036 x1, y1, x2, y2,
1037 uround(calc_distance_i(x1, y1, x2, y2)),
1038 );
1039 let mut sx = sx;
1040 let mut sy = sy;
1041 let mut ex = ex;
1042 let mut ey = ey;
1043 if flags & 1 != 0 {
1044 self.start += uround(
1045 calc_distance_i(lp.x1, lp.y1, x1, y1) / self.scale_x,
1046 );
1047 sx = x1 + (y2 - y1);
1048 sy = y1 - (x2 - x1);
1049 } else {
1050 while (sx - lp.x1).abs() + (sy - lp.y1).abs() > lp2.len {
1051 sx = (lp.x1 + sx) >> 1;
1052 sy = (lp.y1 + sy) >> 1;
1053 }
1054 }
1055 if flags & 2 != 0 {
1056 ex = x2 + (y2 - y1);
1057 ey = y2 - (x2 - x1);
1058 } else {
1059 while (ex - lp.x2).abs() + (ey - lp.y2).abs() > lp2.len {
1060 ex = (lp.x2 + ex) >> 1;
1061 ey = (lp.y2 + ey) >> 1;
1062 }
1063 }
1064 self.line3_no_clip(&lp2, sx, sy, ex, ey);
1065 } else {
1066 self.line3_no_clip(lp, sx, sy, ex, ey);
1067 }
1068 }
1069 self.start = start + uround(lp.len as f64 / self.scale_x);
1070 } else {
1071 self.line3_no_clip(lp, sx, sy, ex, ey);
1072 }
1073 }
1074
1075 fn semidot(&mut self, _cmp: fn(i32) -> bool, _xc1: i32, _yc1: i32, _xc2: i32, _yc2: i32) {}
1076 fn pie(&mut self, _xc: i32, _yc: i32, _x1: i32, _y1: i32, _x2: i32, _y2: i32) {}
1077}
1078
1079fn calc_distance_i(x1: i32, y1: i32, x2: i32, y2: i32) -> f64 {
1084 let dx = (x2 - x1) as f64;
1085 let dy = (y2 - y1) as f64;
1086 (dx * dx + dy * dy).sqrt()
1087}
1088
1089#[cfg(test)]
1094mod tests {
1095 use super::*;
1096
1097 struct SolidPatternSource {
1099 w: u32,
1100 h: u32,
1101 color: Rgba8,
1102 }
1103
1104 impl ImagePatternSource for SolidPatternSource {
1105 fn width(&self) -> f64 { self.w as f64 }
1106 fn height(&self) -> f64 { self.h as f64 }
1107 fn pixel(&self, _x: i32, _y: i32) -> Rgba8 { self.color }
1108 }
1109
1110 #[test]
1111 fn test_line_image_pattern_creation() {
1112 use crate::pattern_filters_rgba::PatternFilterBilinearRgba;
1113 let src = SolidPatternSource { w: 16, h: 8, color: Rgba8::new(255, 0, 0, 255) };
1114 let pat = LineImagePattern::<PatternFilterBilinearRgba>::with_source(&src);
1115 assert!(pat.pattern_width() > 0);
1116 assert!(pat.line_width() > 0);
1117 }
1118
1119 #[test]
1120 fn test_line_image_pattern_pixel() {
1121 use crate::pattern_filters_rgba::PatternFilterNn;
1122 let src = SolidPatternSource { w: 16, h: 8, color: Rgba8::new(255, 128, 64, 200) };
1123 let pat = LineImagePattern::<PatternFilterNn>::with_source(&src);
1124 let mut p = Rgba8::new(0, 0, 0, 0);
1125 pat.pixel(&mut p, 0, 0);
1126 assert!(p.a > 0, "Expected non-zero alpha");
1127 }
1128
1129 #[test]
1130 fn test_distance_interpolator4() {
1131 let di = DistanceInterpolator4::new(
1132 0, 0, 256, 0,
1133 -256, 0, 512, 0,
1134 256, 1.0, 0, 0,
1135 );
1136 assert_ne!(di.len(), 0);
1137 }
1138
1139 #[test]
1140 fn test_line_image_pattern_pow2() {
1141 use crate::pattern_filters_rgba::PatternFilterBilinearRgba;
1142 let src = SolidPatternSource { w: 16, h: 8, color: Rgba8::new(0, 255, 0, 255) };
1143 let pat = LineImagePatternPow2::<PatternFilterBilinearRgba>::with_source(&src);
1144 assert!(pat.pattern_width() > 0);
1145 assert!(pat.line_width() > 0);
1146 let mut p = Rgba8::new(0, 0, 0, 0);
1147 pat.pixel(&mut p, 0, 0);
1148 assert!(p.a > 0);
1149 }
1150
1151 #[test]
1152 fn test_renderer_outline_image_basic() {
1153 use crate::pattern_filters_rgba::PatternFilterBilinearRgba;
1154 use crate::pixfmt_rgba::PixfmtRgba32;
1155 use crate::rendering_buffer::RowAccessor;
1156
1157 let w = 100u32;
1158 let h = 100u32;
1159 let stride = (w * 4) as i32;
1160 let mut buf = vec![255u8; (h * w * 4) as usize];
1161 let mut ra = RowAccessor::new();
1162 unsafe { ra.attach(buf.as_mut_ptr(), w, h, stride); }
1163 let pf = PixfmtRgba32::new(&mut ra);
1164 let mut rb = RendererBase::new(pf);
1165
1166 let src = SolidPatternSource { w: 32, h: 16, color: Rgba8::new(255, 0, 0, 255) };
1167 let pat = LineImagePattern::<PatternFilterBilinearRgba>::with_source(&src);
1168
1169 let mut ren = RendererOutlineImage::new(&mut rb, &pat);
1170 ren.set_scale_x(1.0);
1171 ren.set_start_x(0.0);
1172
1173 let lp = LineParameters::new(
1175 10 * 256, 50 * 256,
1176 90 * 256, 50 * 256,
1177 80 * 256,
1178 );
1179 ren.line3(
1180 &lp,
1181 10 * 256 + (0), 50 * 256 - (80 * 256),
1182 90 * 256 + (0), 50 * 256 - (80 * 256),
1183 );
1184
1185 let mut found = false;
1187 let pf2 = PixfmtRgba32::new(&mut ra);
1188 let rb2 = RendererBase::new(pf2);
1189 for y in 42..=58 {
1190 for x in 10..90 {
1191 let p = rb2.pixel(x, y);
1192 if p.r > 100 {
1193 found = true;
1194 break;
1195 }
1196 }
1197 if found { break; }
1198 }
1199 assert!(found, "Expected red pixels near row 50");
1200 }
1201}