Skip to content

Commit e2d0a43

Browse files
committed
🍯 Added parellisation to compositing.
1 parent fbdb8c6 commit e2d0a43

File tree

2 files changed

+81
-52
lines changed

2 files changed

+81
-52
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ image = "0.24.7"
1414
kamadak-exif = "0.6.1"
1515
num-traits = "0.2.17"
1616
rand = "0.8.5"
17+
rayon = "1.10.0"
1718
serde = { version = "1.0", features = ["derive"] }
1819
serde_bytes = "0.11.12"
1920
serde_json = "1.0.107"

src/composite/compositor.rs

Lines changed: 80 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use std::cmp::min;
22

3+
use rayon::prelude::*;
4+
35
use crate::{BlendMode, Color, Image, Mask, Point};
46

57
use super::blend::{self, RgbaColor};
@@ -59,55 +61,51 @@ pub fn draw_layer_over_image(image: &mut Image, layer: &Layer) {
5961
0
6062
};
6163

62-
let y_offset = if location.y < 0 {
63-
location.y.abs() as u32
64-
} else {
65-
0
66-
};
67-
68-
// I tried using rayon for this, but with 10,000 rows the performance
69-
// was a little worse with rayon than without.
70-
for y in 0..required_height {
71-
let y_position = y + y_offset;
72-
let y_position = (y_position as f32 * pixel_ratio_y).floor() as u32;
73-
let offset = (y_position * layer_bytes_per_row) as usize; //+ y_offset;
74-
let target_offset = ((target_y_offset + y) * image.bytes_per_row) as i32;
75-
let target_offset = (target_offset + (start_x as i32) * 4) as usize;
76-
// Using a second loop was a tiny bit faster than splicing the vec.
77-
for x in 0..required_width {
78-
let alpha = layer.mask_alpha(Point {
79-
x: x as u32,
80-
y: y as u32,
81-
});
82-
if alpha == 0 {
83-
continue;
64+
image
65+
.data
66+
.par_chunks_mut(image.bytes_per_row as usize)
67+
.enumerate()
68+
.skip(target_y_offset as usize) // Skip rows outside the target area
69+
.take(required_height as usize) // Only process the necessary rows
70+
.for_each(|(y, row)| {
71+
let y = y as u32;
72+
let y_position = y as i32 - location.y;
73+
let y_position = (y_position as f32 * pixel_ratio_y).floor() as u32;
74+
let offset = (y_position * layer_bytes_per_row) as usize;
75+
76+
for x in 0..required_width {
77+
let alpha = layer.mask_alpha(Point {
78+
x: x as u32,
79+
y: y - target_y_offset,
80+
});
81+
if alpha == 0 {
82+
continue;
83+
}
84+
let mask_opacity = alpha as f32 / u8::MAX as f32;
85+
let x = x * 4;
86+
let x_position = x + x_offset;
87+
let x_position = (x_position as f32 * pixel_ratio_x).floor() as usize;
88+
let start = offset + x_position;
89+
let blend_color = pixel_data(&layer.image.data, start);
90+
let blend_color: Color = blend_color.into();
91+
92+
let start = start_x as usize * 4 + x;
93+
let base_color = pixel_data(&row, start);
94+
let mut base_color: Color = base_color.into();
95+
96+
blend_colors(
97+
&mut base_color,
98+
&blend_color,
99+
layer.blend_mode,
100+
layer.opacity * mask_opacity,
101+
);
102+
103+
row[start + 0] = base_color.red;
104+
row[start + 1] = base_color.green;
105+
row[start + 2] = base_color.blue;
106+
row[start + 3] = base_color.alpha;
84107
}
85-
let mask_opacity = alpha as f32 / u8::MAX as f32;
86-
let x = x * 4;
87-
let x_position = x + x_offset;
88-
let x_position = (x_position as f32 * pixel_ratio_x).floor() as usize;
89-
let start = offset + x_position;
90-
let blend_color = pixel_data(&layer.image.data, start);
91-
let blend_color: Color = blend_color.into();
92-
93-
let start = target_offset + x;
94-
let base_color = pixel_data(&image.data, start);
95-
let mut base_color: Color = base_color.into();
96-
97-
blend_colors(
98-
&mut base_color,
99-
&blend_color,
100-
layer.blend_mode,
101-
layer.opacity * mask_opacity,
102-
);
103-
// let base_color = Color::RED;
104-
105-
image.data[target_offset + x + 0] = base_color.red;
106-
image.data[target_offset + x + 1] = base_color.green;
107-
image.data[target_offset + x + 2] = base_color.blue;
108-
image.data[target_offset + x + 3] = base_color.alpha;
109-
}
110-
}
108+
});
111109
}
112110

113111
impl Image {
@@ -126,11 +124,12 @@ impl Image {
126124
}
127125

128126
/// Retrieves the pixel data from a given location in a vector of RGBA bytes.
129-
fn pixel_data(source: &Vec<u8>, offset: usize) -> [u8; 4] {
127+
fn pixel_data(source: &[u8], offset: usize) -> [u8; 4] {
130128
source
131129
.get(offset..(offset + 4))
132130
.and_then(|data| data.try_into().ok())
133-
.unwrap_or_default()
131+
.unwrap_or([u8::MAX, 0, 0, u8::MAX])
132+
// .unwrap_or_default()
134133
}
135134

136135
/// Blends one colour with another.
@@ -300,7 +299,7 @@ mod test {
300299
layer.position.x += 1.0;
301300
draw_layer_over_image(&mut base_image, &layer);
302301

303-
// base_image.save("/tmp/tiled_mask.png").unwrap();
302+
base_image.save("/tmp/tiled_mask.png").unwrap();
304303

305304
let expected_image = Image::open("tests/images/tiled_mask.png").unwrap();
306305

@@ -346,10 +345,39 @@ mod test {
346345
layer.position.x -= 1.0;
347346
draw_layer_over_image(&mut base_image, &layer);
348347

349-
// base_image.save("/tmp/tiled_mask_negative.png").unwrap();
348+
base_image.save("/tmp/tiled_mask_negative.png").unwrap();
350349

351350
let expected_image = Image::open("tests/images/tiled_mask_negative.png").unwrap();
352351

353352
assert!(base_image.appears_equal_to(&expected_image));
354353
}
354+
355+
#[test]
356+
#[ignore]
357+
fn draw_layer_performance() {
358+
let size = Size {
359+
width: 10,
360+
height: 10,
361+
};
362+
// let size = Size {
363+
// width: 4,
364+
// height: 4,
365+
// };
366+
let mut base_image = Image::color(&Color::from_rgb_u32(0x639bff), size);
367+
let mut image = Image::color(&Color::from_rgb_u32(0xcbdbfc), size);
368+
image.set_pixel_color(Color::CLEAR, Point { x: 2, y: 1 });
369+
image.set_pixel_color(Color::GREEN, Point { x: 0, y: 0 });
370+
image.set_pixel_color(Color::CYAN, Point { x: 1, y: 0 });
371+
// image.save("/tmp/loco.png").unwrap();
372+
let position = Point { x: 0.0, y: -1.0 };
373+
let layer = Layer::new(&image, position);
374+
375+
let now = std::time::Instant::now();
376+
draw_layer_over_image(&mut base_image, &layer);
377+
// 670ms
378+
println!("🍟 time taken: {:?}", now.elapsed());
379+
380+
base_image.save("/tmp/base_image.png").unwrap();
381+
panic!();
382+
}
355383
}

0 commit comments

Comments
 (0)