1use crate::basics::{CoverType, RectI};
8use crate::pixfmt_rgba::PixelFormat;
9use crate::renderer_base::RendererBase;
10
11pub struct RendererMclip<PF: PixelFormat> {
16 ren: RendererBase<PF>,
17 clip_boxes: Vec<RectI>,
18 curr_cb: usize,
19 bounds: RectI,
20}
21
22impl<PF: PixelFormat> RendererMclip<PF> {
23 pub fn new(ren: RendererBase<PF>) -> Self {
24 let bounds = *ren.clip_box();
25 Self {
26 ren,
27 clip_boxes: Vec::new(),
28 curr_cb: 0,
29 bounds,
30 }
31 }
32
33 pub fn ren(&self) -> &RendererBase<PF> {
34 &self.ren
35 }
36
37 pub fn ren_mut(&mut self) -> &mut RendererBase<PF> {
38 &mut self.ren
39 }
40
41 pub fn add_clip_box(&mut self, x1: i32, y1: i32, x2: i32, y2: i32) {
43 let mut cb = RectI::new(x1, y1, x2, y2);
44 cb.normalize();
45 let buf_rect = RectI::new(
46 0,
47 0,
48 self.ren.width() as i32 - 1,
49 self.ren.height() as i32 - 1,
50 );
51 if cb.clip(&buf_rect) {
52 if self.clip_boxes.is_empty() {
53 self.bounds = cb;
54 } else {
55 if cb.x1 < self.bounds.x1 {
56 self.bounds.x1 = cb.x1;
57 }
58 if cb.y1 < self.bounds.y1 {
59 self.bounds.y1 = cb.y1;
60 }
61 if cb.x2 > self.bounds.x2 {
62 self.bounds.x2 = cb.x2;
63 }
64 if cb.y2 > self.bounds.y2 {
65 self.bounds.y2 = cb.y2;
66 }
67 }
68 self.clip_boxes.push(cb);
69 }
70 }
71
72 pub fn reset_clipping(&mut self, visibility: bool) {
74 self.clip_boxes.clear();
75 if visibility {
76 let w = self.ren.width() as i32;
77 let h = self.ren.height() as i32;
78 self.add_clip_box(0, 0, w - 1, h - 1);
79 }
80 }
81
82 fn first_clip_box(&mut self) -> bool {
84 self.curr_cb = 0;
85 if !self.clip_boxes.is_empty() {
86 let cb = &self.clip_boxes[0];
87 self.ren.clip_box_i(cb.x1, cb.y1, cb.x2, cb.y2);
88 true
89 } else {
90 false
91 }
92 }
93
94 fn next_clip_box(&mut self) -> bool {
96 self.curr_cb += 1;
97 if self.curr_cb < self.clip_boxes.len() {
98 let cb = &self.clip_boxes[self.curr_cb];
99 self.ren.clip_box_i(cb.x1, cb.y1, cb.x2, cb.y2);
100 true
101 } else {
102 false
103 }
104 }
105
106 pub fn bounding_clip_box(&self) -> &RectI {
107 &self.bounds
108 }
109
110 pub fn clip_box_count(&self) -> usize {
111 self.clip_boxes.len()
112 }
113
114 pub fn copy_pixel(&mut self, x: i32, y: i32, c: &PF::ColorType) {
119 if self.first_clip_box() {
120 loop {
121 self.ren.copy_pixel(x, y, c);
122 if !self.next_clip_box() {
123 break;
124 }
125 }
126 }
127 }
128
129 pub fn blend_pixel(&mut self, x: i32, y: i32, c: &PF::ColorType, cover: CoverType) {
130 if self.first_clip_box() {
131 loop {
132 self.ren.blend_pixel(x, y, c, cover);
133 if !self.next_clip_box() {
134 break;
135 }
136 }
137 }
138 }
139
140 pub fn pixel(&self, x: i32, y: i32) -> PF::ColorType
141 where
142 PF::ColorType: Default,
143 {
144 for cb in &self.clip_boxes {
146 if x >= cb.x1 && y >= cb.y1 && x <= cb.x2 && y <= cb.y2 {
147 return self.ren.ren().pixel(x, y);
148 }
149 }
150 PF::ColorType::default()
151 }
152
153 pub fn copy_hline(&mut self, x1: i32, y: i32, x2: i32, c: &PF::ColorType) {
154 if self.first_clip_box() {
155 loop {
156 self.ren.copy_hline(x1, y, x2, c);
157 if !self.next_clip_box() {
158 break;
159 }
160 }
161 }
162 }
163
164 pub fn blend_hline(
165 &mut self,
166 x1: i32,
167 y: i32,
168 x2: i32,
169 c: &PF::ColorType,
170 cover: CoverType,
171 ) {
172 if self.first_clip_box() {
173 loop {
174 self.ren.blend_hline(x1, y, x2, c, cover);
175 if !self.next_clip_box() {
176 break;
177 }
178 }
179 }
180 }
181
182 pub fn blend_vline(
183 &mut self,
184 x: i32,
185 y1: i32,
186 y2: i32,
187 c: &PF::ColorType,
188 cover: CoverType,
189 ) {
190 if self.first_clip_box() {
191 loop {
192 self.ren.blend_vline(x, y1, y2, c, cover);
193 if !self.next_clip_box() {
194 break;
195 }
196 }
197 }
198 }
199
200 pub fn blend_solid_hspan(
201 &mut self,
202 x: i32,
203 y: i32,
204 len: i32,
205 c: &PF::ColorType,
206 covers: &[CoverType],
207 ) {
208 if self.first_clip_box() {
209 loop {
210 self.ren.blend_solid_hspan(x, y, len, c, covers);
211 if !self.next_clip_box() {
212 break;
213 }
214 }
215 }
216 }
217
218 pub fn blend_color_hspan(
219 &mut self,
220 x: i32,
221 y: i32,
222 len: i32,
223 colors: &[PF::ColorType],
224 covers: &[CoverType],
225 cover: CoverType,
226 ) {
227 if self.first_clip_box() {
228 loop {
229 self.ren.blend_color_hspan(x, y, len, colors, covers, cover);
230 if !self.next_clip_box() {
231 break;
232 }
233 }
234 }
235 }
236}
237
238#[cfg(test)]
239mod tests {
240 use super::*;
241 use crate::color::Rgba8;
242 use crate::pixfmt_rgba::PixfmtRgba32;
243 use crate::rendering_buffer::RowAccessor;
244
245 fn make_buffer(w: u32, h: u32) -> (Vec<u8>, RowAccessor) {
246 let stride = (w * 4) as i32;
247 let buf = vec![0u8; (h * w * 4) as usize];
248 let mut ra = RowAccessor::new();
249 unsafe {
250 ra.attach(buf.as_ptr() as *mut u8, w, h, stride);
251 }
252 (buf, ra)
253 }
254
255 #[test]
256 fn test_add_clip_boxes() {
257 let (_buf, mut ra) = make_buffer(200, 200);
258 let pixf = PixfmtRgba32::new(&mut ra);
259 let ren = RendererBase::new(pixf);
260 let mut mclip = RendererMclip::new(ren);
261
262 mclip.add_clip_box(10, 10, 50, 50);
263 mclip.add_clip_box(100, 100, 150, 150);
264 assert_eq!(mclip.clip_box_count(), 2);
265
266 let b = mclip.bounding_clip_box();
267 assert_eq!(b.x1, 10);
268 assert_eq!(b.y1, 10);
269 assert_eq!(b.x2, 150);
270 assert_eq!(b.y2, 150);
271 }
272
273 #[test]
274 fn test_render_to_multiple_clips() {
275 let (_buf, mut ra) = make_buffer(100, 100);
276 let pixf = PixfmtRgba32::new(&mut ra);
277 let ren = RendererBase::new(pixf);
278 let mut mclip = RendererMclip::new(ren);
279
280 mclip.add_clip_box(0, 0, 49, 49);
281 mclip.add_clip_box(50, 50, 99, 99);
282
283 let red = Rgba8::new(255, 0, 0, 255);
284 mclip.copy_pixel(25, 25, &red); mclip.copy_pixel(75, 75, &red); let p1 = mclip.pixel(25, 25);
288 assert_eq!(p1.r, 255);
289 let p2 = mclip.pixel(75, 75);
290 assert_eq!(p2.r, 255);
291 }
292
293 #[test]
294 fn test_reset_clipping() {
295 let (_buf, mut ra) = make_buffer(100, 100);
296 let pixf = PixfmtRgba32::new(&mut ra);
297 let ren = RendererBase::new(pixf);
298 let mut mclip = RendererMclip::new(ren);
299
300 mclip.add_clip_box(10, 10, 50, 50);
301 assert_eq!(mclip.clip_box_count(), 1);
302
303 mclip.reset_clipping(false);
304 assert_eq!(mclip.clip_box_count(), 0);
305
306 mclip.reset_clipping(true);
307 assert_eq!(mclip.clip_box_count(), 1);
308 }
309}