1use image::{ImageBuffer, Luma, Pixel};
2use imageproc::drawing::draw_filled_circle_mut;
3use serde::{ser::SerializeStruct, Deserialize, Serialize};
4use std::mem;
5
6use crate::{color_with_intensity, result::RvResult, rverr, OutOfBoundsMode, ShapeI};
7
8use super::{
9 line::render_line, BbF, BbI, BrushLine, Point, PtF, PtI, RenderTargetOrShape, TPtF, TPtI,
10};
11
12fn line_to_mask(
13 line: &BrushLine,
14 orig_shape: Option<ShapeI>,
15 buffer: Option<Vec<u8>>,
16) -> RvResult<(Vec<u8>, BbI)> {
17 let bb = line.bb(orig_shape)?;
18 let color = Luma([1]);
19 let bbi = BbI::from_arr(&[
20 bb.x as u32,
21 bb.y as u32,
22 bb.w.ceil() as u32,
23 bb.h.ceil() as u32,
24 ]);
25 let is_none = buffer.is_none();
26 let mut buffer = if let Some(mut buffer) =
27 buffer.filter(|buffer| buffer.len() >= (bbi.w * bbi.h) as usize)
28 {
29 buffer.fill(0);
30 ImageBuffer::from_vec(bbi.w, bbi.h, buffer)
31 .unwrap_or_else(|| RenderTargetOrShape::Shape(bbi.shape()).make_buffer())
32 } else {
33 tracing::debug!(
34 "(re-)creating buffer, buffer is {}",
35 if is_none { "None" } else { "Some" }
36 );
37 RenderTargetOrShape::Shape(bbi.shape()).make_buffer()
38 };
39 let im = if line.line.points.len() == 1 {
40 let center = Point {
41 x: (line.line.points[0].x - bb.x) as i32,
42 y: (line.line.points[0].y - bb.y) as i32,
43 };
44
45 let thickness_half = line.thickness * 0.5;
46 if line.thickness <= 1.1 {
47 buffer.put_pixel(center.x as u32, center.y as u32, color);
48 } else {
49 let r = if thickness_half.floor() == thickness_half {
50 (thickness_half - 1.0) as i32
51 } else {
52 thickness_half as i32
53 };
54 draw_filled_circle_mut(&mut buffer, (center.x, center.y), r, color);
55 }
56 buffer
57 } else {
58 render_line(
59 line.line
60 .points_iter()
61 .filter(|p| bb.contains(*p))
62 .map(|p| PtF {
63 x: p.x - bb.x,
64 y: p.y - bb.y,
65 }),
66 1.0,
67 line.thickness,
68 RenderTargetOrShape::Image(buffer),
69 color,
70 )
71 };
72 Ok((im.to_vec(), bbi))
73}
74
75#[must_use]
76pub fn mask_to_rle(mask: &[u8], mask_w: u32, mask_h: u32) -> Vec<u32> {
77 let mut rle = Vec::new();
78 let mut current_run = 0;
79 let mut current_value = 0;
80 for y in 0..mask_h {
81 for x in 0..mask_w {
82 let value = mask[(y * mask_w + x) as usize];
83 if value == current_value {
84 current_run += 1;
85 } else {
86 rle.push(current_run);
87 current_run = 1;
88 current_value = value;
89 }
90 }
91 }
92 rle.push(current_run);
93 rle
94}
95
96pub fn rle_to_mask_inplace(rle: &[u32], mask: &mut [u8], w: u32) {
97 for (i, &run) in rle.iter().enumerate() {
98 let value = i % 2;
99 let start = rle.iter().take(i).sum::<u32>();
100 for idx in start..(start + run) {
101 let x = idx % w;
102 let y = idx / w;
103 let idx = (y * w + x) as usize;
104 if idx < mask.len() {
105 mask[idx] = value as u8;
106 }
107 }
108 }
109}
110
111#[must_use]
112pub fn rle_to_mask(rle: &[u32], w: u32, h: u32) -> Vec<u8> {
113 let mut mask = vec![0; (w * h) as usize];
114 rle_to_mask_inplace(rle, &mut mask, w);
115 mask
116}
117
118fn idx_bb_to_pixim(idx_bb: u32, bb: BbI) -> PtI {
119 PtI {
120 y: idx_bb / bb.w,
121 x: idx_bb % bb.w,
122 } + bb.min()
123}
124
125fn idx_bb_to_im(idx_bb: u32, bb: BbI, w_im: TPtI) -> u32 {
126 let p_im = idx_bb_to_pixim(idx_bb, bb);
127 p_im.y * w_im + p_im.x
128}
129
130fn idx_im_to_bb(idx_im: u32, bb: BbI, w_im: TPtI) -> Option<u32> {
131 let p_im = PtI {
132 x: idx_im % w_im,
133 y: idx_im / w_im,
134 };
135 if bb.contains(p_im) {
136 let p = p_im - bb.min();
137 Some(p.y * bb.w + p.x)
138 } else {
139 None
140 }
141}
142pub fn rle_bb_to_image(rle_bb: &[u32], bb: BbI, shape_im: ShapeI) -> RvResult<Vec<u32>> {
145 if !bb.is_contained_in_image(shape_im) {
146 Err(rverr!(
147 "Bounding box {} is not contained in image with shape {:?}",
148 bb,
149 shape_im
150 ))
151 } else {
152 if rle_bb.len() == 1 {
154 return Ok(vec![shape_im.w * shape_im.h]);
155 }
156 let n_zero_rows = rle_bb[0] / bb.w;
158 let bb = BbI::from_arr(&[bb.x, bb.y + n_zero_rows, bb.w, bb.h - n_zero_rows]);
159 let rle_0_correction = n_zero_rows * bb.w;
160 let n_zero_rows = if rle_bb.len() % 2 == 1 {
162 rle_bb.iter().last().unwrap() / bb.w
163 } else {
164 0
165 };
166 let bb = BbI::from_arr(&[bb.x, bb.y, bb.w, bb.h - n_zero_rows]);
167 let rle_1_correction = n_zero_rows * bb.w;
168
169 let mut rle_im = vec![];
170 let offset = idx_bb_to_im(0, bb, shape_im.w);
171 rle_im.push(offset + rle_bb[0] - rle_0_correction);
172 let mut prev_idx = rle_im[0] - 1;
173 for i in 1..rle_bb.len() {
174 let sum_correction = rle_0_correction
175 + if i == rle_bb.len() - 1 {
176 rle_1_correction
177 } else {
178 0
179 };
180 let im_idx = idx_bb_to_im(
181 rle_bb[..=i].iter().sum::<u32>() - 1 - sum_correction,
182 bb,
183 shape_im.w,
184 );
185 let p = PtI {
186 x: im_idx % shape_im.w,
187 y: im_idx / shape_im.w,
188 };
189 let p_prev = PtI {
190 x: prev_idx % shape_im.w,
191 y: prev_idx / shape_im.w,
192 };
193 let is_foreground_run = i % 2 == 1;
194 let row_span = p.y - p_prev.y;
195 if is_foreground_run {
196 if row_span == 0 {
197 rle_im.push(p.x - p_prev.x);
198 } else {
199 let n_elts = bb.max().x - p_prev.x;
200 if n_elts > 0 {
202 rle_im.push(n_elts);
203 for _ in 0..(row_span - 1) {
204 rle_im.push(shape_im.w - bb.w);
205 rle_im.push(bb.w);
206 }
207 rle_im.push(shape_im.w - bb.w);
208 }
209 rle_im.push(p.x + 1 - bb.x);
210 }
211 if i == rle_bb.len() - 1 {
212 rle_im.push(
213 bb.x + bb.w - 1 - p.x + shape_im.w * (shape_im.h - p.y - 1) + shape_im.w
214 - (bb.w + bb.x),
215 );
216 }
217 } else {
218 let n_elts = if row_span == 0 {
219 p.x - p_prev.x
220 } else {
221 bb.x_max() + 1 - p_prev.x + (row_span - 1) * shape_im.w + shape_im.w - bb.w
222 + p.x
223 - bb.x
224 };
225 let n_elts = if p.x == bb.x_max() && i < rle_bb.len() - 1 {
226 n_elts + shape_im.w - bb.w
227 } else {
228 n_elts
229 };
230 let n_elts = if i == rle_bb.len() - 1 {
231 n_elts + shape_im.w - (bb.w + bb.x) + shape_im.w * (shape_im.h - p.y - 1)
232 } else {
233 n_elts
234 };
235 rle_im.push(n_elts);
236 }
237 prev_idx = im_idx;
238 }
239 Ok(rle_im)
240 }
241}
242pub fn rle_image_to_bb(rle_im: &[u32], bb: BbI, shape_im: ShapeI) -> RvResult<Vec<u32>> {
245 if !bb.is_contained_in_image(shape_im) {
246 Err(rverr!(
247 "Bounding box {} is not contained in image with shape {:?}",
248 bb,
249 shape_im
250 ))
251 } else {
252 if rle_im.len() == 1 {
254 return Ok(vec![bb.w * bb.h]);
255 }
256 let mut mask = vec![0; (bb.w * bb.h) as usize];
257
258 for (i, run) in rle_im.iter().enumerate() {
259 let is_foreground_run = i % 2 == 1;
260 if is_foreground_run {
261 let start = rle_im.iter().take(i).sum::<u32>();
262 for idx in start..(start + run) {
263 if let Some(idx_bb) = idx_im_to_bb(idx, bb, shape_im.w) {
264 mask[idx_bb as usize] = 1;
265 }
266 }
267 }
268 }
269 Ok(mask_to_rle(&mask, bb.w, bb.h))
270 }
271}
272
273pub fn access_bb_idx(bb: BbI, p: PtI) -> usize {
275 if bb.contains(p) {
276 ((p.y - bb.y) * bb.w + p.x - bb.x) as usize
277 } else {
278 0
279 }
280}
281
282#[must_use]
284pub fn access_mask_abs(mask: &[u8], bb: BbI, p: PtI) -> u8 {
285 if bb.contains(p) {
286 mask[access_bb_idx(bb, p)]
287 } else {
288 0
289 }
290}
291#[must_use]
292pub fn access_mask_rel(mask: &[u8], x: u32, y: u32, w: u32, h: u32) -> u8 {
293 if x < w && y < h {
294 mask[(y * w + x) as usize]
295 } else {
296 0
297 }
298}
299
300#[derive(Clone, Debug, Default, PartialEq)]
301pub struct Canvas {
302 pub mask: Vec<u8>,
303 pub bb: BbI,
304 pub intensity: TPtF,
305}
306
307impl Canvas {
308 pub fn from_line_extended(
309 line: &BrushLine,
310 orig_shape: ShapeI,
311 extension_factor: f64,
312 lower_buffer_bound: usize,
313 ) -> RvResult<Self> {
314 if extension_factor < 1.0 {
315 return Err(rverr!("extension factor {extension_factor} smaller 1"));
316 }
317 let bb = line.bb(Some(orig_shape))?;
318 let new_w = (bb.w * extension_factor).ceil() as usize;
319 let new_h = (bb.h * extension_factor).ceil() as usize;
320 let new_size = (new_w * new_h)
321 .min(orig_shape.w as usize * orig_shape.h as usize)
322 .max(lower_buffer_bound);
323 let buffer = vec![0; new_size];
324 Self::new(line, orig_shape, Some(buffer))
325 }
326 pub fn new(line: &BrushLine, orig_shape: ShapeI, buffer: Option<Vec<u8>>) -> RvResult<Self> {
327 let (mask, bb) = line_to_mask(line, Some(orig_shape), buffer)?;
328 Ok(Self {
329 mask,
330 bb,
331 intensity: line.intensity,
332 })
333 }
334 pub fn from_box(bb: BbI, intensity: TPtF) -> Self {
335 Self {
336 bb,
337 mask: vec![1; (bb.w * bb.h) as usize],
338 intensity,
339 }
340 }
341 #[must_use]
342 pub fn merge(mut self, other: &Canvas) -> Self {
343 let old_self_bb = self.bb;
344 self.bb = self.bb.merge(other.bb);
345 self.mask.resize((self.bb.w * self.bb.h) as usize, 0);
346 for y in (0..old_self_bb.h).rev() {
348 for x in (0..old_self_bb.w).rev() {
349 let p = PtI { x, y } + old_self_bb.min();
350 let old_idx = (y * old_self_bb.w + x) as usize;
351 let new_idx = access_bb_idx(self.bb, p);
352 let val = self.mask[old_idx];
353 self.mask[old_idx] = 0;
354 self.mask[new_idx] = val;
355 }
356 }
357 for y in 0..other.bb.h {
359 for x in 0..other.bb.w {
360 let p = PtI { x, y } + other.bb.min();
361 let val_self = access_mask_abs(&self.mask, self.bb, p);
362 let val_other = other.mask[(y * other.bb.w + x) as usize];
363 let val = val_self.max(val_other);
364 self.mask[((p.y - self.bb.y) * self.bb.w + (p.x - self.bb.x)) as usize] = val;
365 }
366 }
367 self.intensity = self.intensity.max(other.intensity);
368 self
369 }
370 pub fn draw_circle(&mut self, center: PtF, thickness: TPtF, color: u8) -> RvResult<()> {
371 let im = ImageBuffer::<Luma<u8>, Vec<u8>>::from_vec(
372 self.bb.w,
373 self.bb.h,
374 mem::take(&mut self.mask),
375 );
376 if let Some(mut im) = im {
377 let color = Luma([color]);
378 let center = Point {
379 x: (center.x - TPtF::from(self.bb.x)) as i32,
380 y: (center.y - TPtF::from(self.bb.y)) as i32,
381 };
382
383 if thickness <= 1.1 {
384 im.put_pixel(center.x as u32, center.y as u32, color);
385 } else {
386 draw_filled_circle_mut(
387 &mut im,
388 (center.x, center.y),
389 (thickness * 0.5) as i32,
390 color,
391 );
392 }
393 self.mask = im.into_vec();
394 Ok(())
395 } else {
396 Err(rverr!(
397 "Could not create image buffer for canvas at {:?}",
398 self.bb
399 ))
400 }
401 }
402 pub fn from_serialized_brush_line(bl: &BrushLine) -> RvResult<Self> {
404 let (mask, bb) = line_to_mask(bl, None, None)?;
405 Ok(Self {
406 mask,
407 bb,
408 intensity: bl.intensity,
409 })
410 }
411 pub fn follow_movement(&mut self, from: PtF, to: PtF, shape: ShapeI) {
412 let x_shift = (to.x - from.x) as TPtF;
413 let y_shift = (to.y - from.y) as TPtF;
414 let bb: BbF = self.bb.into();
415 let bb = bb.translate(x_shift, y_shift, shape, OutOfBoundsMode::Deny);
416 if let Some(bb) = bb {
417 self.bb = bb.into();
418 }
419 }
420}
421
422impl Serialize for Canvas {
423 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
424 where
425 S: serde::Serializer,
426 {
427 let mut state = serializer.serialize_struct("Canvas", 3)?;
428 state.serialize_field("rle", &mask_to_rle(&self.mask, self.bb.w, self.bb.h))?;
429 state.serialize_field("bb", &self.bb)?;
430 state.serialize_field("intensity", &self.intensity)?;
431 state.end()
432 }
433}
434impl<'de> Deserialize<'de> for Canvas {
435 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
436 where
437 D: serde::Deserializer<'de>,
438 {
439 #[derive(Deserialize)]
440 struct CanvasDe {
441 rle: Vec<u32>,
442 bb: BbI,
443 intensity: TPtF,
444 }
445 #[derive(Deserialize)]
446 #[serde(untagged)]
447 enum CanvasOrBl {
448 Canvas(CanvasDe),
449 BrushLine(BrushLine),
450 }
451 let read = CanvasOrBl::deserialize(deserializer)?;
452 match read {
453 CanvasOrBl::Canvas(canvas_de) => {
454 let mask = rle_to_mask(&canvas_de.rle, canvas_de.bb.w, canvas_de.bb.h);
455 Ok(Self {
456 mask,
457 bb: canvas_de.bb,
458 intensity: canvas_de.intensity,
459 })
460 }
461 CanvasOrBl::BrushLine(bl) => {
462 Canvas::from_serialized_brush_line(&bl).map_err(serde::de::Error::custom)
463 }
464 }
465 }
466}
467
468pub fn canvases_to_image<'a, CLR>(
469 canvases: impl Iterator<Item = &'a Canvas>,
470 image_or_shape: RenderTargetOrShape<CLR>,
471 color: CLR,
472) -> ImageBuffer<CLR, Vec<u8>>
473where
474 CLR: Pixel<Subpixel = u8>,
475{
476 let mut im = image_or_shape.make_buffer();
477 for cv in canvases {
478 let color = color_with_intensity(color, cv.intensity);
479 for y in cv.bb.y_range() {
480 for x in cv.bb.x_range() {
481 let p_idx = PtI { x, y };
482 let is_fg = access_mask_abs(&cv.mask, cv.bb, p_idx) > 0;
483 if is_fg {
484 im.put_pixel(x, y, color);
485 }
486 }
487 }
488 }
489 im
490}
491
492#[cfg(test)]
493use super::{Line, BB};
494#[test]
495fn test_canvas_single() {
496 let orig_shape = ShapeI::new(30, 30);
497 let bl = BrushLine {
498 line: Line {
499 points: vec![PtF { x: 5.0, y: 5.0 }],
500 },
501 intensity: 0.5,
502 thickness: 3.0,
503 };
504 let cv = Canvas::new(&bl, orig_shape, None).unwrap();
505 assert!(cv.mask.iter().sum::<u8>() > 0);
506 let buffer = vec![43; 100];
507 let orig_shape = ShapeI::new(30, 30);
508 let bl = BrushLine {
509 line: Line {
510 points: vec![PtF { x: 5.0, y: 5.0 }],
511 },
512 intensity: 0.5,
513 thickness: 3.0,
514 };
515 let cv2 = Canvas::new(&bl, orig_shape, Some(buffer)).unwrap();
516 assert_eq!(cv.mask.iter().sum::<u8>(), cv2.mask.iter().sum::<u8>());
517}
518
519#[test]
520fn test_rle() {
521 fn test(bb: BbI, shape: ShapeI, rle_bb: &[u32], rle_im_ref: &[u32], skip_rec: bool) {
522 let rle_im = rle_bb_to_image(rle_bb, bb, shape).unwrap();
523 assert_eq!(rle_im, rle_im_ref);
524 assert_eq!(rle_im.iter().sum::<u32>(), shape.w * shape.h);
525 let rle_bb_rec = rle_image_to_bb(&rle_im, bb, shape).unwrap();
526 if !skip_rec {
527 assert_eq!(rle_bb_rec, rle_bb);
528 }
529 }
530 let rle_bb = [1, 1, 4, 1, 1];
531 let bb = BbI::from_arr(&[1, 1, 2, 4]);
532 let shape = ShapeI::new(4, 6);
533 let rle_im_ref = [6, 1, 10, 1, 6];
534 test(bb, shape, &rle_bb, &rle_im_ref, false);
535
536 let rle_bb = [0, 3, 1, 2];
537 let bb = BbI::from_arr(&[3, 2, 2, 3]);
538 let shape = ShapeI::new(6, 6);
539 let rle_im_ref = [15, 2, 4, 1, 5, 2, 7];
540 test(bb, shape, &rle_bb, &rle_im_ref, false);
541
542 let rle_bb = [0, 1, 3];
543 let bb = BbI::from_arr(&[2, 2, 2, 2]);
544 let shape = ShapeI::new(6, 6);
545 let rle_im_ref = [14, 1, 21];
546 test(bb, shape, &rle_bb, &rle_im_ref, true);
547
548 let rle_bb = [1, 2, 1];
549 let bb = BbI::from_arr(&[1, 1, 2, 2]);
550 let shape = ShapeI::new(6, 4);
551 let rle_im_ref = [8, 1, 4, 1, 10];
552 test(bb, shape, &rle_bb, &rle_im_ref, false);
553
554 let rle_bb = vec![0, 2, 2, 2];
555 let bb = BbI::from_arr(&[3, 2, 2, 3]);
556 let shape = ShapeI::new(6, 6);
557 let rle_im_ref = vec![15, 2, 10, 2, 7];
558 test(bb, shape, &rle_bb, &rle_im_ref, false);
559
560 let rle_bb = vec![3, 1];
561 let bb = BbI::from_arr(&[1, 1, 2, 2]);
562 let shape = ShapeI::new(6, 6);
563 let rle_im_ref = vec![14, 1, 21];
564 test(bb, shape, &rle_bb, &rle_im_ref, true);
565
566 let rle_bb = vec![6];
567 let bb = BbI::from_arr(&[2, 1, 2, 3]);
568 let shape = ShapeI::new(6, 6);
569 let rle_im_ref = vec![36];
570 test(bb, shape, &rle_bb, &rle_im_ref, false);
571
572 let rle_bb = vec![0, 6];
573 let bb = BbI::from_arr(&[2, 1, 2, 3]);
574 let shape = ShapeI::new(6, 6);
575 let rle_im_ref = vec![8, 2, 4, 2, 4, 2, 14];
576 test(bb, shape, &rle_bb, &rle_im_ref, false);
577
578 let rle_bb = vec![0, 1, 2, 1];
579 let bb = BbI::from_arr(&[1, 1, 2, 2]);
580 let shape = ShapeI::new(6, 8);
581 let rle_im_ref = vec![7, 1, 6, 1, 33];
582 test(bb, shape, &rle_bb, &rle_im_ref, false);
583
584 let rle_bb = vec![1, 4, 1];
585 let bb = BbI::from_arr(&[1, 1, 2, 3]);
586 let shape = ShapeI::new(6, 6);
587 let rle_im_ref = vec![8, 1, 4, 2, 4, 1, 16];
588 test(bb, shape, &rle_bb, &rle_im_ref, false);
589
590 let mask = vec![0, 1, 0, 0, 0, 0, 1, 0];
591 let rle = mask_to_rle(&mask, 2, 4);
592 assert_eq!(rle, vec![1, 1, 4, 1, 1]);
593
594 let mask = vec![0, 0, 0, 0, 0, 0, 0, 0, 0];
595 let rle = mask_to_rle(&mask, 3, 3);
596 assert_eq!(rle, vec![9]);
597 let mask2 = rle_to_mask(&rle, 3, 3);
598 assert_eq!(mask, mask2);
599
600 let mask = vec![1, 1, 1, 1, 1, 1, 1, 1, 1];
601 let rle = mask_to_rle(&mask, 3, 3);
602 assert_eq!(rle, vec![0, 9]);
603 let mask2 = rle_to_mask(&rle, 3, 3);
604 assert_eq!(mask, mask2);
605
606 let mask = vec![1, 0, 0, 1, 1, 1, 0, 0, 0];
607 let rle = mask_to_rle(&mask, 3, 3);
608 assert_eq!(rle, vec![0, 1, 2, 3, 3]);
609 let mask2 = rle_to_mask(&rle, 3, 3);
610 assert_eq!(mask, mask2);
611
612 let bb = BbI::from_arr(&[5, 10, 4, 8]);
613 let shape_im = ShapeI::new(100, 200);
614 let x = idx_bb_to_im(0, bb, shape_im.w);
615 assert_eq!(x, 1005);
616 let x = idx_bb_to_im(1, bb, shape_im.w);
617 assert_eq!(x, 1006);
618 let x = idx_bb_to_im(3, bb, shape_im.w);
619 assert_eq!(x, 1008);
620}
621
622#[test]
623fn test_canvas_serde() {
624 let orig_shape = ShapeI::new(30, 30);
625 let bl = BrushLine {
626 line: Line {
627 points: vec![PtF { x: 5.0, y: 5.0 }],
628 },
629 intensity: 0.5,
630 thickness: 3.0,
631 };
632 let cv = Canvas::new(&bl, orig_shape, None).unwrap();
633 let s = serde_json::to_string(&cv).unwrap();
634 let cv_read: Canvas = serde_json::from_str(&s).unwrap();
635 assert_eq!(cv, cv_read);
636}
637
638#[test]
639fn test_line_to_mask() {
640 fn test(mask_zeros: &[u8], mask_sum: u8, bb: BbI, bl: &BrushLine) {
641 let (mask2, bb2) = line_to_mask(bl, None, None).unwrap();
642
643 assert_eq!(bb, bb2);
644 assert_eq!(mask2.iter().sum::<u8>(), mask_sum);
645 for i in mask_zeros {
646 assert_eq!(mask2[*i as usize], 0);
647 }
648 }
649
650 let bl = BrushLine {
651 line: Line {
652 points: vec![PtF { x: 4.7, y: 4.7 }],
653 },
654 intensity: 0.5,
655 thickness: 3.0,
656 };
657 test(&[0, 2, 6, 8], 5, BB::from_arr(&[3, 3, 3, 3]), &bl);
658
659 let bl = BrushLine {
660 line: Line {
661 points: vec![PtF { x: 5.3, y: 5.3 }],
662 },
663 intensity: 0.5,
664 thickness: 3.0,
665 };
666 test(&[0, 2, 6, 8], 5, BB::from_arr(&[3, 3, 3, 3]), &bl);
667 let bl = BrushLine {
668 line: Line {
669 points: vec![PtF { x: 5.0, y: 5.0 }],
670 },
671 intensity: 0.5,
672 thickness: 3.0,
673 };
674 test(&[0, 2, 6, 8], 5, BB::from_arr(&[3, 3, 3, 3]), &bl);
675 let center = PtF { x: 5.0, y: 5.0 };
676 let bl = BrushLine {
677 line: Line {
678 points: vec![center],
679 },
680 intensity: 0.5,
681 thickness: 5.0,
682 };
683 test(&[], 21, BB::from_arr(&[2, 2, 5, 5]), &bl);
684 let mut canvas = Canvas::new(&bl, ShapeI::new(30, 30), None).unwrap();
685 canvas.draw_circle(center, 5.0, 0).unwrap();
686 assert!(canvas.mask.iter().sum::<u8>() < 21 / 2);
688}
689
690#[test]
691fn test_merge() {
692 let c1 = Canvas {
693 bb: BbI::from_arr(&[0, 0, 2, 2]),
694 mask: vec![1, 0, 0, 1],
695 intensity: 0.5,
696 };
697 let c2 = Canvas {
698 bb: BbI::from_arr(&[0, 0, 2, 2]),
699 mask: vec![1, 0, 0, 1],
700 intensity: 0.7,
701 };
702 let merged = c1.clone().merge(&c2);
703 assert_eq!(merged.mask, c1.mask);
704 assert_eq!(merged.bb, c1.bb);
705 assert_eq!(merged.intensity, c2.intensity);
706 let c1 = Canvas {
707 bb: BbI::from_arr(&[0, 0, 2, 2]),
708 mask: vec![0, 1, 1, 0],
709 intensity: 0.5,
710 };
711 let c2 = Canvas {
712 bb: BbI::from_arr(&[0, 0, 2, 2]),
713 mask: vec![1, 0, 0, 1],
714 intensity: 0.7,
715 };
716 let merged = c1.merge(&c2);
717 assert_eq!(merged.mask, vec![1, 1, 1, 1]);
718
719 let c1 = Canvas {
720 bb: BbI::from_arr(&[0, 0, 3, 2]),
721 mask: vec![1, 0, 0, 0, 1, 0],
722 intensity: 0.5,
723 };
724 let c2 = Canvas {
725 bb: BbI::from_arr(&[2, 2, 2, 2]),
726 mask: vec![1, 1, 1, 1],
727 intensity: 0.7,
728 };
729 let merged = c1.merge(&c2);
730 assert_eq!(c2.intensity, merged.intensity);
731 assert_eq!(merged.mask.len(), 16);
732 assert_eq!(merged.bb, BbI::from_arr(&[0, 0, 4, 4]));
733 let mask_reference = vec![1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1];
734 assert_eq!(merged.mask, mask_reference);
735
736 let c1 = Canvas {
737 bb: BbI::from_arr(&[1, 1, 3, 2]),
738 mask: vec![1, 0, 0, 0, 1, 0],
739 intensity: 0.5,
740 };
741 let c2 = Canvas {
742 bb: BbI::from_arr(&[3, 3, 2, 2]),
743 mask: vec![1, 1, 1, 1],
744 intensity: 0.7,
745 };
746 let merged = c1.merge(&c2);
747 assert_eq!(c2.intensity, merged.intensity);
748 assert_eq!(merged.mask.len(), 16);
749 assert_eq!(merged.bb, BbI::from_arr(&[1, 1, 4, 4]));
750 let mask_reference = vec![1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1];
751 assert_eq!(merged.mask, mask_reference);
752}
753
754#[test]
755fn test_from_box() {
756 let bb = BbI::from_arr(&[7, 8, 2, 2]);
757 let i = 1.0;
758 let c = Canvas::from_box(bb, i);
759 assert_eq!(c.mask.len(), (bb.w * bb.h) as usize);
760 assert_eq!(c.bb, bb);
761 assert_eq!(c.intensity, i);
762}