1use crate::basics::{CoverType, RectI};
7use crate::pixfmt_rgba::PixelFormat;
8
9pub struct RendererBase<PF: PixelFormat> {
18 ren: PF,
19 clip_box: RectI,
20}
21
22impl<PF: PixelFormat> RendererBase<PF> {
23 pub fn new(ren: PF) -> Self {
26 let w = ren.width() as i32;
27 let h = ren.height() as i32;
28 Self {
29 ren,
30 clip_box: RectI::new(0, 0, w - 1, h - 1),
31 }
32 }
33
34 pub fn width(&self) -> u32 {
35 self.ren.width()
36 }
37 pub fn height(&self) -> u32 {
38 self.ren.height()
39 }
40
41 pub fn clip_box_i(&mut self, x1: i32, y1: i32, x2: i32, y2: i32) -> bool {
43 let mut cb = RectI::new(x1, y1, x2, y2);
44 cb.normalize();
45 if cb.clip(&RectI::new(
46 0,
47 0,
48 self.ren.width() as i32 - 1,
49 self.ren.height() as i32 - 1,
50 )) {
51 self.clip_box = cb;
52 true
53 } else {
54 self.clip_box.x1 = 1;
55 self.clip_box.y1 = 1;
56 self.clip_box.x2 = 0;
57 self.clip_box.y2 = 0;
58 false
59 }
60 }
61
62 pub fn reset_clipping(&mut self, visibility: bool) {
64 if visibility {
65 self.clip_box.x1 = 0;
66 self.clip_box.y1 = 0;
67 self.clip_box.x2 = self.ren.width() as i32 - 1;
68 self.clip_box.y2 = self.ren.height() as i32 - 1;
69 } else {
70 self.clip_box.x1 = 1;
71 self.clip_box.y1 = 1;
72 self.clip_box.x2 = 0;
73 self.clip_box.y2 = 0;
74 }
75 }
76
77 pub fn clip_box(&self) -> &RectI {
78 &self.clip_box
79 }
80 pub fn xmin(&self) -> i32 {
81 self.clip_box.x1
82 }
83 pub fn ymin(&self) -> i32 {
84 self.clip_box.y1
85 }
86 pub fn xmax(&self) -> i32 {
87 self.clip_box.x2
88 }
89 pub fn ymax(&self) -> i32 {
90 self.clip_box.y2
91 }
92
93 #[inline]
94 pub fn inbox(&self, x: i32, y: i32) -> bool {
95 x >= self.clip_box.x1
96 && y >= self.clip_box.y1
97 && x <= self.clip_box.x2
98 && y <= self.clip_box.y2
99 }
100
101 pub fn ren(&self) -> &PF {
103 &self.ren
104 }
105
106 pub fn ren_mut(&mut self) -> &mut PF {
108 &mut self.ren
109 }
110
111 pub fn clear(&mut self, c: &PF::ColorType) {
117 let w = self.ren.width();
118 if w > 0 {
119 let h = self.ren.height();
120 for y in 0..h as i32 {
121 self.ren.copy_hline(0, y, w, c);
122 }
123 }
124 }
125
126 pub fn copy_pixel(&mut self, x: i32, y: i32, c: &PF::ColorType) {
128 if self.inbox(x, y) {
129 self.ren.copy_pixel(x, y, c);
130 }
131 }
132
133 pub fn blend_pixel(&mut self, x: i32, y: i32, c: &PF::ColorType, cover: CoverType) {
135 if self.inbox(x, y) {
136 self.ren.blend_pixel(x, y, c, cover);
137 }
138 }
139
140 pub fn pixel(&self, x: i32, y: i32) -> PF::ColorType
142 where
143 PF::ColorType: Default,
144 {
145 if self.inbox(x, y) {
146 self.ren.pixel(x, y)
147 } else {
148 PF::ColorType::default()
149 }
150 }
151
152 pub fn copy_hline(&mut self, mut x1: i32, y: i32, mut x2: i32, c: &PF::ColorType) {
154 if x1 > x2 {
155 std::mem::swap(&mut x1, &mut x2);
156 }
157 if y > self.ymax() || y < self.ymin() || x1 > self.xmax() || x2 < self.xmin() {
158 return;
159 }
160 x1 = x1.max(self.xmin());
161 x2 = x2.min(self.xmax());
162 self.ren.copy_hline(x1, y, (x2 - x1 + 1) as u32, c);
163 }
164
165 pub fn blend_hline(
167 &mut self,
168 mut x1: i32,
169 y: i32,
170 mut x2: i32,
171 c: &PF::ColorType,
172 cover: CoverType,
173 ) {
174 if x1 > x2 {
175 std::mem::swap(&mut x1, &mut x2);
176 }
177 if y > self.ymax() || y < self.ymin() || x1 > self.xmax() || x2 < self.xmin() {
178 return;
179 }
180 x1 = x1.max(self.xmin());
181 x2 = x2.min(self.xmax());
182 self.ren.blend_hline(x1, y, (x2 - x1 + 1) as u32, c, cover);
183 }
184
185 pub fn blend_vline(
187 &mut self,
188 x: i32,
189 mut y1: i32,
190 mut y2: i32,
191 c: &PF::ColorType,
192 cover: CoverType,
193 ) {
194 if y1 > y2 {
195 std::mem::swap(&mut y1, &mut y2);
196 }
197 if x > self.xmax() || x < self.xmin() || y1 > self.ymax() || y2 < self.ymin() {
198 return;
199 }
200 y1 = y1.max(self.ymin());
201 y2 = y2.min(self.ymax());
202 for y in y1..=y2 {
203 self.ren.blend_pixel(x, y, c, cover);
204 }
205 }
206
207 pub fn blend_bar(
209 &mut self,
210 x1: i32,
211 y1: i32,
212 x2: i32,
213 y2: i32,
214 c: &PF::ColorType,
215 cover: CoverType,
216 ) {
217 let mut rc = crate::basics::RectI::new(x1, y1, x2, y2);
218 rc.normalize();
219 if !rc.clip(&self.clip_box) {
220 return;
221 }
222 for y in rc.y1..=rc.y2 {
223 self.ren
224 .blend_hline(rc.x1, y, (rc.x2 - rc.x1 + 1) as u32, c, cover);
225 }
226 }
227
228 pub fn blend_solid_hspan(
230 &mut self,
231 mut x: i32,
232 y: i32,
233 mut len: i32,
234 c: &PF::ColorType,
235 covers: &[CoverType],
236 ) {
237 if y > self.ymax() || y < self.ymin() {
238 return;
239 }
240
241 let mut covers_offset = 0usize;
242 if x < self.xmin() {
243 let d = self.xmin() - x;
244 len -= d;
245 if len <= 0 {
246 return;
247 }
248 covers_offset += d as usize;
249 x = self.xmin();
250 }
251 if x + len > self.xmax() + 1 {
252 len = self.xmax() - x + 1;
253 if len <= 0 {
254 return;
255 }
256 }
257 self.ren
258 .blend_solid_hspan(x, y, len as u32, c, &covers[covers_offset..]);
259 }
260
261 pub fn blend_solid_vspan(
263 &mut self,
264 x: i32,
265 mut y: i32,
266 mut len: i32,
267 c: &PF::ColorType,
268 covers: &[CoverType],
269 ) {
270 if x > self.xmax() || x < self.xmin() {
271 return;
272 }
273
274 let mut covers_offset = 0usize;
275 if y < self.ymin() {
276 let d = self.ymin() - y;
277 len -= d;
278 if len <= 0 {
279 return;
280 }
281 covers_offset += d as usize;
282 y = self.ymin();
283 }
284 if y + len > self.ymax() + 1 {
285 len = self.ymax() - y + 1;
286 if len <= 0 {
287 return;
288 }
289 }
290 for i in 0..len as usize {
291 self.ren
292 .blend_pixel(x, y + i as i32, c, covers[covers_offset + i]);
293 }
294 }
295
296 pub fn blend_color_hspan(
301 &mut self,
302 mut x: i32,
303 y: i32,
304 mut len: i32,
305 colors: &[PF::ColorType],
306 covers: &[CoverType],
307 cover: CoverType,
308 ) {
309 if y > self.ymax() || y < self.ymin() {
310 return;
311 }
312
313 let mut colors_offset = 0usize;
314 let mut covers_offset = 0usize;
315 if x < self.xmin() {
316 let d = (self.xmin() - x) as usize;
317 len -= d as i32;
318 if len <= 0 {
319 return;
320 }
321 if !covers.is_empty() {
322 covers_offset += d;
323 }
324 colors_offset += d;
325 x = self.xmin();
326 }
327 if x + len > self.xmax() + 1 {
328 len = self.xmax() - x + 1;
329 if len <= 0 {
330 return;
331 }
332 }
333 self.ren.blend_color_hspan(
334 x,
335 y,
336 len as u32,
337 &colors[colors_offset..],
338 if covers.is_empty() {
339 &[]
340 } else {
341 &covers[covers_offset..]
342 },
343 cover,
344 );
345 }
346
347 pub fn blend_color_vspan(
354 &mut self,
355 x: i32,
356 mut y: i32,
357 mut len: i32,
358 colors: &[PF::ColorType],
359 covers: &[CoverType],
360 cover: CoverType,
361 ) {
362 if x > self.xmax() || x < self.xmin() {
363 return;
364 }
365
366 let mut colors_offset = 0usize;
367 let mut covers_offset = 0usize;
368 if y < self.ymin() {
369 let d = (self.ymin() - y) as usize;
370 len -= d as i32;
371 if len <= 0 {
372 return;
373 }
374 if !covers.is_empty() {
375 covers_offset += d;
376 }
377 colors_offset += d;
378 y = self.ymin();
379 }
380 if y + len > self.ymax() + 1 {
381 len = self.ymax() - y + 1;
382 if len <= 0 {
383 return;
384 }
385 }
386 for i in 0..len as usize {
388 let c = if covers.is_empty() { cover } else { covers[covers_offset + i] };
389 self.ren.blend_pixel(x, y + i as i32, &colors[colors_offset + i], c);
390 }
391 }
392}
393
394#[cfg(test)]
399mod tests {
400 use super::*;
401 use crate::color::Rgba8;
402 use crate::pixfmt_rgba::PixfmtRgba32;
403 use crate::rendering_buffer::RowAccessor;
404
405 const BPP: usize = 4;
406
407 fn make_renderer(w: u32, h: u32) -> (Vec<u8>, RowAccessor) {
408 let stride = (w * BPP as u32) as i32;
409 let buf = vec![0u8; (h * w * BPP as u32) as usize];
410 let mut ra = RowAccessor::new();
411 unsafe {
412 ra.attach(buf.as_ptr() as *mut u8, w, h, stride);
413 }
414 (buf, ra)
415 }
416
417 #[test]
418 fn test_new() {
419 let (_buf, mut ra) = make_renderer(100, 80);
420 let pf = PixfmtRgba32::new(&mut ra);
421 let ren = RendererBase::new(pf);
422 assert_eq!(ren.width(), 100);
423 assert_eq!(ren.height(), 80);
424 assert_eq!(ren.xmin(), 0);
425 assert_eq!(ren.ymin(), 0);
426 assert_eq!(ren.xmax(), 99);
427 assert_eq!(ren.ymax(), 79);
428 }
429
430 #[test]
431 fn test_clear() {
432 let (_buf, mut ra) = make_renderer(10, 10);
433 let pf = PixfmtRgba32::new(&mut ra);
434 let mut ren = RendererBase::new(pf);
435 let white = Rgba8::new(255, 255, 255, 255);
436 ren.clear(&white);
437 let p = ren.ren().pixel(5, 5);
438 assert_eq!(p.r, 255);
439 assert_eq!(p.a, 255);
440 }
441
442 #[test]
443 fn test_copy_pixel_clipped() {
444 let (_buf, mut ra) = make_renderer(10, 10);
445 let pf = PixfmtRgba32::new(&mut ra);
446 let mut ren = RendererBase::new(pf);
447 let red = Rgba8::new(255, 0, 0, 255);
448 ren.copy_pixel(5, 5, &red);
450 assert_eq!(ren.ren().pixel(5, 5).r, 255);
451 ren.copy_pixel(-1, 5, &red);
453 ren.copy_pixel(100, 5, &red);
454 }
455
456 #[test]
457 fn test_blend_hline_clipped() {
458 let (_buf, mut ra) = make_renderer(20, 10);
459 let pf = PixfmtRgba32::new(&mut ra);
460 let mut ren = RendererBase::new(pf);
461 let green = Rgba8::new(0, 255, 0, 255);
462 ren.blend_hline(15, 5, 25, &green, 255);
464 assert_eq!(ren.ren().pixel(15, 5).g, 255);
466 assert_eq!(ren.ren().pixel(19, 5).g, 255);
467 }
468
469 #[test]
470 fn test_clip_box() {
471 let (_buf, mut ra) = make_renderer(100, 100);
472 let pf = PixfmtRgba32::new(&mut ra);
473 let mut ren = RendererBase::new(pf);
474 assert!(ren.clip_box_i(10, 10, 50, 50));
475 assert_eq!(ren.xmin(), 10);
476 assert_eq!(ren.ymin(), 10);
477 assert_eq!(ren.xmax(), 50);
478 assert_eq!(ren.ymax(), 50);
479 }
480
481 #[test]
482 fn test_clip_box_invalid() {
483 let (_buf, mut ra) = make_renderer(100, 100);
484 let pf = PixfmtRgba32::new(&mut ra);
485 let mut ren = RendererBase::new(pf);
486 assert!(!ren.clip_box_i(200, 200, 300, 300));
488 }
489
490 #[test]
491 fn test_blend_solid_hspan_clipped() {
492 let (_buf, mut ra) = make_renderer(20, 10);
493 let pf = PixfmtRgba32::new(&mut ra);
494 let mut ren = RendererBase::new(pf);
495 let blue = Rgba8::new(0, 0, 255, 255);
496 let covers = vec![255u8; 10];
498 ren.blend_solid_hspan(-3, 5, 10, &blue, &covers);
499 assert_eq!(ren.ren().pixel(0, 5).b, 255);
501 assert_eq!(ren.ren().pixel(6, 5).b, 255);
502 }
503
504 #[test]
505 fn test_blend_solid_hspan_fully_clipped() {
506 let (_buf, mut ra) = make_renderer(20, 10);
507 let pf = PixfmtRgba32::new(&mut ra);
508 let mut ren = RendererBase::new(pf);
509 let red = Rgba8::new(255, 0, 0, 255);
510 let covers = [255u8; 5];
511 ren.blend_solid_hspan(5, -1, 5, &red, &covers);
513 assert_eq!(ren.ren().pixel(5, 0).r, 0);
515 }
516
517 #[test]
518 fn test_inbox() {
519 let (_buf, mut ra) = make_renderer(10, 10);
520 let pf = PixfmtRgba32::new(&mut ra);
521 let ren = RendererBase::new(pf);
522 assert!(ren.inbox(0, 0));
523 assert!(ren.inbox(9, 9));
524 assert!(!ren.inbox(-1, 0));
525 assert!(!ren.inbox(10, 0));
526 assert!(!ren.inbox(0, 10));
527 }
528
529 #[test]
530 fn test_reset_clipping() {
531 let (_buf, mut ra) = make_renderer(100, 100);
532 let pf = PixfmtRgba32::new(&mut ra);
533 let mut ren = RendererBase::new(pf);
534 ren.clip_box_i(10, 10, 50, 50);
535 ren.reset_clipping(true);
536 assert_eq!(ren.xmin(), 0);
537 assert_eq!(ren.ymin(), 0);
538 assert_eq!(ren.xmax(), 99);
539 assert_eq!(ren.ymax(), 99);
540
541 ren.reset_clipping(false);
542 assert!(!ren.inbox(0, 0));
543 }
544
545 #[test]
546 fn test_blend_color_hspan() {
547 let (_buf, mut ra) = make_renderer(20, 10);
548 let pf = PixfmtRgba32::new(&mut ra);
549 let mut ren = RendererBase::new(pf);
550 let colors = [
551 Rgba8::new(255, 0, 0, 255),
552 Rgba8::new(0, 255, 0, 255),
553 Rgba8::new(0, 0, 255, 255),
554 ];
555 ren.blend_color_hspan(5, 3, 3, &colors, &[], 255);
556 let p0 = ren.ren().pixel(5, 3);
557 assert_eq!(p0.r, 255);
558 let p1 = ren.ren().pixel(6, 3);
559 assert_eq!(p1.g, 255);
560 let p2 = ren.ren().pixel(7, 3);
561 assert_eq!(p2.b, 255);
562 }
563
564 #[test]
565 fn test_blend_color_hspan_clipped_left() {
566 let (_buf, mut ra) = make_renderer(20, 10);
567 let pf = PixfmtRgba32::new(&mut ra);
568 let mut ren = RendererBase::new(pf);
569 let colors = [
570 Rgba8::new(255, 0, 0, 255),
571 Rgba8::new(0, 255, 0, 255),
572 Rgba8::new(0, 0, 255, 255),
573 ];
574 ren.blend_color_hspan(-1, 3, 3, &colors, &[], 255);
576 let p0 = ren.ren().pixel(0, 3);
579 assert_eq!(p0.g, 255);
580 let p1 = ren.ren().pixel(1, 3);
582 assert_eq!(p1.b, 255);
583 }
584
585 #[test]
586 fn test_blend_color_hspan_clipped_y() {
587 let (_buf, mut ra) = make_renderer(20, 10);
588 let pf = PixfmtRgba32::new(&mut ra);
589 let mut ren = RendererBase::new(pf);
590 let colors = [Rgba8::new(255, 0, 0, 255)];
591 ren.blend_color_hspan(5, -1, 1, &colors, &[], 255);
593 let p = ren.ren().pixel(5, 0);
594 assert_eq!(p.r, 0);
595 }
596}