Auto merge of #6355 - hyowon:drawing_shadows, r=nox

https://html.spec.whatwg.org/multipage/#when-shadows-are-drawn
r? @nox 
cc @mmatyas @yichoi

<!-- Reviewable:start -->
[<img src="https://reviewable.io/review_button.png" height=40 alt="Review on Reviewable"/>](https://reviewable.io/reviews/servo/servo/6355)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2015-06-16 05:30:20 -06:00
commit 48e1d45bff
30 changed files with 134 additions and 204 deletions

View file

@ -54,40 +54,6 @@ impl<'a> CanvasPaintTask<'a> {
image_data
}
/// It writes image data to the canvas
/// source_rect: the area of the image data to be written
/// dest_rect: The area of the canvas where the imagedata will be copied
/// smoothing_enabled: if smoothing is applied to the copied pixels
fn write_pixels(&self, imagedata: &[u8],
image_size: Size2D<f64>,
source_rect: Rect<f64>,
dest_rect: Rect<f64>,
smoothing_enabled: bool) {
// From spec https://html.spec.whatwg.org/multipage/#dom-context-2d-drawimage
// When scaling up, if the imageSmoothingEnabled attribute is set to true, the user agent should attempt
// to apply a smoothing algorithm to the image data when it is scaled.
// Otherwise, the image must be rendered using nearest-neighbor interpolation.
let filter = if smoothing_enabled {
Filter::Linear
} else {
Filter::Point
};
// azure_hl operates with integers. We need to cast the image size
let image_size = image_size.to_i32();
let source_surface = self.drawtarget.create_source_surface_from_data(
&imagedata,
image_size, image_size.width * 4, SurfaceFormat::B8G8R8A8);
let draw_surface_options = DrawSurfaceOptions::new(filter, true);
let draw_options = DrawOptions::new(self.state.draw_options.alpha, CompositionOp::Over, AntialiasMode::None);
self.drawtarget.draw_surface(source_surface,
dest_rect.to_azfloat(),
source_rect.to_azfloat(),
draw_surface_options, draw_options);
}
/// dirty_rect: original dirty_rect provided by the putImageData call
/// image_data_rect: the area of the image to be copied
/// Result: It retuns the modified dirty_rect by the rules described in
@ -136,23 +102,6 @@ impl<'a> CanvasPaintTask<'a> {
dirty_rect
}
/// It writes an image to the destination canvas
/// imagedata: Pixel information of the image to be written. It takes RGBA8
/// image_size: The size of the image to be written
/// dest_rect: Area of the destination canvas where the pixels will be copied
/// smoothing_enabled: It determines if smoothing is applied to the image result
fn write_image(&self, mut imagedata: Vec<u8>,
image_size: Size2D<f64>, dest_rect: Rect<f64>, smoothing_enabled: bool) {
if imagedata.len() == 0 {
return
}
let image_rect = Rect::new(Point2D::new(0f64, 0f64), image_size);
// rgba -> bgra
byte_swap(&mut imagedata);
self.write_pixels(&imagedata, image_size, image_rect, dest_rect, smoothing_enabled);
}
}
pub struct CanvasPaintTask<'a> {
@ -316,8 +265,15 @@ impl<'a> CanvasPaintTask<'a> {
}
);
self.drawtarget.fill_rect(&draw_rect, self.state.fill_style.to_pattern_ref(),
Some(&self.state.draw_options));
if self.need_to_draw_shadow() {
self.draw_with_shadow(&draw_rect, |new_draw_target: &DrawTarget| {
new_draw_target.fill_rect(&draw_rect, self.state.fill_style.to_pattern_ref(),
Some(&self.state.draw_options));
});
} else {
self.drawtarget.fill_rect(&draw_rect, self.state.fill_style.to_pattern_ref(),
Some(&self.state.draw_options));
}
}
fn clear_rect(&self, rect: &Rect<f32>) {
@ -376,7 +332,19 @@ impl<'a> CanvasPaintTask<'a> {
let source_rect = source_rect.ceil();
// It discards the extra pixels (if any) that won't be painted
let image_data = crop_image(image_data, image_size, source_rect);
self.write_image(image_data, source_rect.size, dest_rect, smoothing_enabled);
if self.need_to_draw_shadow() {
let rect = Rect::new(Point2D::new(dest_rect.origin.x as f32, dest_rect.origin.y as f32),
Size2D::new(dest_rect.size.width as f32, dest_rect.size.height as f32));
self.draw_with_shadow(&rect, |new_draw_target: &DrawTarget| {
write_image(&new_draw_target, image_data, source_rect.size, dest_rect,
smoothing_enabled, self.state.draw_options.alpha);
});
} else {
write_image(&self.drawtarget, image_data, source_rect.size, dest_rect,
smoothing_enabled, self.state.draw_options.alpha);
}
}
fn draw_image_self(&self, image_size: Size2D<f64>,
@ -384,9 +352,21 @@ impl<'a> CanvasPaintTask<'a> {
smoothing_enabled: bool) {
// Reads pixels from source image
// In this case source and target are the same canvas
let imagedata = self.read_pixels(source_rect, image_size);
// Writes on target canvas
self.write_image(imagedata, image_size, dest_rect, smoothing_enabled);
let image_data = self.read_pixels(source_rect, image_size);
if self.need_to_draw_shadow() {
let rect = Rect::new(Point2D::new(dest_rect.origin.x as f32, dest_rect.origin.y as f32),
Size2D::new(dest_rect.size.width as f32, dest_rect.size.height as f32));
self.draw_with_shadow(&rect, |new_draw_target: &DrawTarget| {
write_image(&new_draw_target, image_data, source_rect.size, dest_rect,
smoothing_enabled, self.state.draw_options.alpha);
});
} else {
// Writes on target canvas
write_image(&self.drawtarget, image_data, image_size, dest_rect,
smoothing_enabled, self.state.draw_options.alpha);
}
}
fn move_to(&self, point: &Point2D<AzFloat>) {
@ -577,7 +557,7 @@ impl<'a> CanvasPaintTask<'a> {
// rgba -> bgra
byte_swap(&mut imagedata);
let image_rect = Rect::new(Point2D::new(0f64, 0f64),
let image_rect = Rect::new(Point2D::zero(),
Size2D::new(image_data_rect.size.width, image_data_rect.size.height));
// Dirty rectangle defines the area of the source image to be copied
@ -607,7 +587,8 @@ impl<'a> CanvasPaintTask<'a> {
image_data_rect.origin.y + source_rect.origin.y),
Size2D::new(source_rect.size.width, source_rect.size.height));
self.write_pixels(&imagedata, image_data_rect.size, source_rect, dest_rect, true)
write_pixels(&self.drawtarget, &imagedata, image_data_rect.size, source_rect,
dest_rect, true, self.state.draw_options.alpha)
}
fn set_shadow_offset_x(&mut self, value: f64) {
@ -625,6 +606,41 @@ impl<'a> CanvasPaintTask<'a> {
fn set_shadow_color(&mut self, value: AzColor) {
self.state.shadow_color = value;
}
// https://html.spec.whatwg.org/multipage/#when-shadows-are-drawn
fn need_to_draw_shadow(&self) -> bool {
self.state.shadow_color.a != 0.0f32 &&
(self.state.shadow_offset_x != 0.0f64 ||
self.state.shadow_offset_y != 0.0f64 ||
self.state.shadow_blur != 0.0f64)
}
fn create_draw_target_for_shadow(&self, source_rect: &Rect<f32>) -> DrawTarget {
let draw_target = self.drawtarget.create_similar_draw_target(&Size2D::new(source_rect.size.width as i32,
source_rect.size.height as i32),
self.drawtarget.get_format());
let matrix = Matrix2D::identity().translate(-source_rect.origin.x as AzFloat,
-source_rect.origin.y as AzFloat)
.mul(&self.state.transform);
draw_target.set_transform(&matrix);
draw_target
}
fn draw_with_shadow<F>(&self, rect: &Rect<f32>, draw_shadow_source: F)
where F: FnOnce(&DrawTarget)
{
let shadow_src_rect = self.state.transform.transform_rect(rect);
let new_draw_target = self.create_draw_target_for_shadow(&shadow_src_rect);
draw_shadow_source(&new_draw_target);
self.drawtarget.draw_surface_with_shadow(new_draw_target.snapshot(),
&Point2D::new(shadow_src_rect.origin.x as AzFloat,
shadow_src_rect.origin.y as AzFloat),
&self.state.shadow_color,
&Point2D::new(self.state.shadow_offset_x as AzFloat,
self.state.shadow_offset_y as AzFloat),
(self.state.shadow_blur / 2.0f64) as AzFloat,
self.state.draw_options.composition);
}
}
/// Used by drawImage to get rid of the extra pixels of the image data that
@ -658,6 +674,65 @@ fn crop_image(image_data: Vec<u8>,
new_image_data
}
/// It writes an image to the destination target
/// draw_target: the destination target where the image_data will be copied
/// image_data: Pixel information of the image to be written. It takes RGBA8
/// image_size: The size of the image to be written
/// dest_rect: Area of the destination target where the pixels will be copied
/// smoothing_enabled: It determines if smoothing is applied to the image result
fn write_image(draw_target: &DrawTarget,
mut image_data: Vec<u8>,
image_size: Size2D<f64>,
dest_rect: Rect<f64>,
smoothing_enabled: bool,
global_alpha: f32) {
if image_data.len() == 0 {
return
}
let image_rect = Rect::new(Point2D::zero(), image_size);
// rgba -> bgra
byte_swap(&mut image_data);
write_pixels(&draw_target, &image_data, image_size, image_rect, dest_rect, smoothing_enabled, global_alpha);
}
/// It writes image data to the target
/// draw_target: the destination target where the imagedata will be copied
/// source_rect: the area of the image data to be written
/// dest_rect: The area of the target where the imagedata will be copied
/// smoothing_enabled: if smoothing is applied to the copied pixels
fn write_pixels(draw_target: &DrawTarget,
image_data: &[u8],
image_size: Size2D<f64>,
source_rect: Rect<f64>,
dest_rect: Rect<f64>,
smoothing_enabled: bool,
global_alpha: f32) {
// From spec https://html.spec.whatwg.org/multipage/#dom-context-2d-drawimage
// When scaling up, if the imageSmoothingEnabled attribute is set to true, the user agent should attempt
// to apply a smoothing algorithm to the image data when it is scaled.
// Otherwise, the image must be rendered using nearest-neighbor interpolation.
let filter = if smoothing_enabled {
Filter::Linear
} else {
Filter::Point
};
// azure_hl operates with integers. We need to cast the image size
let image_size = image_size.to_i32();
let source_surface = draw_target.create_source_surface_from_data(
&image_data,
image_size, image_size.width * 4, SurfaceFormat::B8G8R8A8);
let draw_surface_options = DrawSurfaceOptions::new(filter, true);
let draw_options = DrawOptions::new(global_alpha, CompositionOp::Over, AntialiasMode::None);
draw_target.draw_surface(source_surface,
dest_rect.to_azfloat(),
source_rect.to_azfloat(),
draw_surface_options,
draw_options);
}
pub trait SizeToi32 {
fn to_i32(&self) -> Size2D<i32>;
}