diff --git a/piet-cairo/src/lib.rs b/piet-cairo/src/lib.rs index 246768ef..e14117cf 100644 --- a/piet-cairo/src/lib.rs +++ b/piet-cairo/src/lib.rs @@ -390,7 +390,115 @@ impl<'a> RenderContext for CairoRenderContext<'a> { _image: &Self::Image, _blur_radius: f64, ) -> Result { - 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 = 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)) } }