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