Skip to content

Commit

Permalink
Reimplement Cairo blur method, slightly slower but safer than the pre…
Browse files Browse the repository at this point in the history
…vious implementation.
  • Loading branch information
longmathemagician committed Aug 19, 2022
1 parent b2154e5 commit 422ea81
Showing 1 changed file with 109 additions and 1 deletion.
110 changes: 109 additions & 1 deletion piet-cairo/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,115 @@ impl<'a> RenderContext for CairoRenderContext<'a> {
_image: &Self::Image,
_blur_radius: f64,
) -> Result<Self::Image, Error> {
Err(Error::Unimplemented)
// Set image parameters
let size = _image.size();
let width = size.width as usize;
let height = size.height as usize;
let image_channel_count: usize = 4;

// Calcuale blur parameters
let blur_radius_min: usize = 2;
let blur_increment_size: usize = 3;
let blur_iterations: usize =
(_blur_radius as usize - blur_radius_min) / blur_increment_size;

// Create a new image surface to hold the blurred image
let mut target_surface = ImageSurface::create(Format::ARgb32, width as i32, height as i32)
.map_err(convert_error)?;

// Encapsulate drawing context so that the pointer reference to the
// image surface is dropped before we request the data
{
let target_ctx = Context::new(&target_surface).map_err(convert_error)?;
target_ctx
.set_source_surface(&_image.0, 0.0, 0.0)
.map_err(convert_error)?;
target_ctx.rectangle(0.0, 0.0, size.width, size.height);
target_ctx.fill().map_err(convert_error)?;
}

// Encapsulate blur so that borrows are dropped before returning the image
{
let mut blurred_surface_data = target_surface
.data()
.expect("Failed to address surface data");
let mut inner_blur_loop = |current_pass_radius: usize| {
let original_surface_data_copy: Vec<u8> = blurred_surface_data.to_vec();

// precompute loop constants
let edge_offset = current_pass_radius * image_channel_count;
let width_offset_adjusted =
width * image_channel_count - edge_offset - image_channel_count;
let height_offset_adjusted =
height * image_channel_count - edge_offset - image_channel_count;
let current_pass_radius_offset = current_pass_radius * image_channel_count;

//parse colum elements (down)
for j in (0..(height * image_channel_count)).step_by(image_channel_count) {
//parse row elements (to the right)
for i in (0..(width * image_channel_count)).step_by(image_channel_count) {
for k in 0..(image_channel_count - 1) {
let x = if edge_offset > i {
if width_offset_adjusted > edge_offset {
edge_offset
} else {
width_offset_adjusted
}
} else {
if width_offset_adjusted > i {
i
} else {
width_offset_adjusted
}
};
// let x = (width_offset_adjusted).min(edge_offset.max(i));
let y = (height_offset_adjusted).min((edge_offset).max(j));

let mut sum: f32 = 0.; // channel accumulator

// add the channel value from the current pixel
sum += (original_surface_data_copy[k + y * width + x] as f32) * 0.25;

// add the channel values from the surrounding four corner pixels at radius distance
sum += original_surface_data_copy[k
+ (y - current_pass_radius_offset) * width
+ x
- current_pass_radius_offset]
as f32
* 0.1875; // top left pixel
sum += original_surface_data_copy[k
+ (y - current_pass_radius_offset) * width
+ x
+ current_pass_radius_offset]
as f32
* 0.1875; // top right pixel
sum += original_surface_data_copy[k
+ (y + current_pass_radius_offset) * width
+ x
- current_pass_radius_offset]
as f32
* 0.1875; // bottom left pixel
sum += original_surface_data_copy[k
+ (y + current_pass_radius_offset) * width
+ x
+ current_pass_radius_offset]
as f32
* 0.1875; // bottom right pixel

blurred_surface_data[k + j * width + i] = sum as u8;
}
}
}
};

for i in (blur_radius_min..(blur_increment_size * blur_iterations))
.step_by(blur_increment_size)
{
inner_blur_loop(i);
}
}

Ok(CairoImage(target_surface))
}
}

Expand Down

0 comments on commit 422ea81

Please sign in to comment.