mirror of
https://github.com/servo/servo.git
synced 2025-08-02 20:20:14 +01:00
auto merge of #4582 : pcwalton/servo/blend-modes, r=glennw
`background-blend-mode` is not yet supported because we don't support multiple backgrounds yet. r? @glennw
This commit is contained in:
commit
e6fe9f1409
10 changed files with 181 additions and 23 deletions
|
@ -41,7 +41,7 @@ use std::fmt;
|
|||
use std::slice::Items;
|
||||
use std::sync::Arc;
|
||||
use style::ComputedValues;
|
||||
use style::computed_values::{border_style, cursor, filter, pointer_events};
|
||||
use style::computed_values::{border_style, cursor, filter, mix_blend_mode, pointer_events};
|
||||
|
||||
// It seems cleaner to have layout code not mention Azure directly, so let's just reexport this for
|
||||
// layout to use.
|
||||
|
@ -165,6 +165,8 @@ pub struct StackingContext {
|
|||
pub z_index: i32,
|
||||
/// CSS filters to be applied to this stacking context (including opacity).
|
||||
pub filters: filter::T,
|
||||
/// The blend mode with which this stacking context blends with its backdrop.
|
||||
pub blend_mode: mix_blend_mode::T,
|
||||
}
|
||||
|
||||
impl StackingContext {
|
||||
|
@ -175,6 +177,7 @@ impl StackingContext {
|
|||
overflow: &Rect<Au>,
|
||||
z_index: i32,
|
||||
filters: filter::T,
|
||||
blend_mode: mix_blend_mode::T,
|
||||
layer: Option<Arc<PaintLayer>>)
|
||||
-> StackingContext {
|
||||
StackingContext {
|
||||
|
@ -184,6 +187,7 @@ impl StackingContext {
|
|||
overflow: *overflow,
|
||||
z_index: z_index,
|
||||
filters: filters,
|
||||
blend_mode: blend_mode,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -194,7 +198,7 @@ impl StackingContext {
|
|||
transform: &Matrix2D<AzFloat>,
|
||||
clip_rect: Option<&Rect<Au>>) {
|
||||
let temporary_draw_target =
|
||||
paint_context.get_or_create_temporary_draw_target(&self.filters);
|
||||
paint_context.get_or_create_temporary_draw_target(&self.filters, self.blend_mode);
|
||||
{
|
||||
let mut paint_subcontext = PaintContext {
|
||||
draw_target: temporary_draw_target.clone(),
|
||||
|
@ -307,7 +311,8 @@ impl StackingContext {
|
|||
}
|
||||
|
||||
paint_context.draw_temporary_draw_target_if_necessary(&temporary_draw_target,
|
||||
&self.filters)
|
||||
&self.filters,
|
||||
self.blend_mode)
|
||||
}
|
||||
|
||||
/// Translate the given tile rect into the coordinate system of a child stacking context.
|
||||
|
@ -849,7 +854,7 @@ impl DisplayItem {
|
|||
bounds.origin.y = bounds.origin.y + y_offset;
|
||||
bounds.size = image_item.stretch_size;
|
||||
|
||||
paint_context.draw_image(bounds, image_item.image.clone());
|
||||
paint_context.draw_image(&bounds, image_item.image.clone());
|
||||
|
||||
x_offset = x_offset + image_item.stretch_size.width;
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ use std::f32;
|
|||
use std::mem;
|
||||
use std::num::{Float, FloatMath};
|
||||
use std::ptr;
|
||||
use style::computed_values::{border_style, filter};
|
||||
use style::computed_values::{border_style, filter, mix_blend_mode};
|
||||
use std::sync::Arc;
|
||||
use text::TextRun;
|
||||
use text::glyph::CharIndex;
|
||||
|
@ -124,7 +124,7 @@ impl<'a> PaintContext<'a> {
|
|||
self.draw_target.pop_clip();
|
||||
}
|
||||
|
||||
pub fn draw_image(&self, bounds: Rect<Au>, image: Arc<Box<Image>>) {
|
||||
pub fn draw_image(&self, bounds: &Rect<Au>, image: Arc<Box<Image>>) {
|
||||
let size = Size2D(image.width as i32, image.height as i32);
|
||||
let (pixel_width, pixels, source_format) = match image.pixels {
|
||||
PixelsByColorType::RGBA8(ref pixels) => (4, pixels.as_slice(), SurfaceFormat::B8G8R8A8),
|
||||
|
@ -864,15 +864,18 @@ impl<'a> PaintContext<'a> {
|
|||
&end_point.to_azure_point(),
|
||||
stops,
|
||||
&Matrix2D::identity());
|
||||
|
||||
self.draw_target.fill_rect(&bounds.to_azure_rect(),
|
||||
PatternRef::LinearGradient(&pattern),
|
||||
None);
|
||||
}
|
||||
|
||||
pub fn get_or_create_temporary_draw_target(&mut self, filters: &filter::T) -> DrawTarget {
|
||||
pub fn get_or_create_temporary_draw_target(&mut self,
|
||||
filters: &filter::T,
|
||||
blend_mode: mix_blend_mode::T)
|
||||
-> DrawTarget {
|
||||
// Determine if we need a temporary draw target.
|
||||
if !filters::temporary_draw_target_needed_for_style_filters(filters) {
|
||||
if !filters::temporary_draw_target_needed_for_style_filters(filters) &&
|
||||
blend_mode == mix_blend_mode::T::normal {
|
||||
// Reuse the draw target, but remove the transient clip. If we don't do the latter,
|
||||
// we'll be in a state whereby the paint subcontext thinks it has no transient clip
|
||||
// (see `StackingContext::optimize_and_draw_into_context`) but it actually does,
|
||||
|
@ -896,7 +899,8 @@ impl<'a> PaintContext<'a> {
|
|||
/// after doing all the painting, and the temporary draw target must not be used afterward.
|
||||
pub fn draw_temporary_draw_target_if_necessary(&mut self,
|
||||
temporary_draw_target: &DrawTarget,
|
||||
filters: &filter::T) {
|
||||
filters: &filter::T,
|
||||
blend_mode: mix_blend_mode::T) {
|
||||
if (*temporary_draw_target) == self.draw_target {
|
||||
// We're directly painting to the surface; nothing to do.
|
||||
return
|
||||
|
@ -914,11 +918,9 @@ impl<'a> PaintContext<'a> {
|
|||
|
||||
// Perform the blit operation.
|
||||
let rect = Rect(Point2D(0.0, 0.0), self.draw_target.get_size().to_azure_size());
|
||||
let draw_options = DrawOptions::new(opacity, 0);
|
||||
self.draw_target.draw_filter(&filter_node,
|
||||
&rect,
|
||||
&rect.origin,
|
||||
draw_options);
|
||||
let mut draw_options = DrawOptions::new(opacity, 0);
|
||||
draw_options.set_composition_op(blend_mode.to_azure_composition_op());
|
||||
self.draw_target.draw_filter(&filter_node, &rect, &rect.origin, draw_options);
|
||||
self.draw_target.set_transform(&old_transform);
|
||||
}
|
||||
|
||||
|
@ -1261,3 +1263,32 @@ impl DrawTargetExtensions for DrawTarget {
|
|||
}
|
||||
}
|
||||
|
||||
/// Converts a CSS blend mode (per CSS-COMPOSITING) to an Azure `CompositionOp`.
|
||||
trait ToAzureCompositionOp {
|
||||
/// Converts a CSS blend mode (per CSS-COMPOSITING) to an Azure `CompositionOp`.
|
||||
fn to_azure_composition_op(&self) -> CompositionOp;
|
||||
}
|
||||
|
||||
impl ToAzureCompositionOp for mix_blend_mode::T {
|
||||
fn to_azure_composition_op(&self) -> CompositionOp {
|
||||
match *self {
|
||||
mix_blend_mode::T::normal => CompositionOp::Over,
|
||||
mix_blend_mode::T::multiply => CompositionOp::Multiply,
|
||||
mix_blend_mode::T::screen => CompositionOp::Screen,
|
||||
mix_blend_mode::T::overlay => CompositionOp::Overlay,
|
||||
mix_blend_mode::T::darken => CompositionOp::Darken,
|
||||
mix_blend_mode::T::lighten => CompositionOp::Lighten,
|
||||
mix_blend_mode::T::color_dodge => CompositionOp::ColorDodge,
|
||||
mix_blend_mode::T::color_burn => CompositionOp::ColorBurn,
|
||||
mix_blend_mode::T::hard_light => CompositionOp::HardLight,
|
||||
mix_blend_mode::T::soft_light => CompositionOp::SoftLight,
|
||||
mix_blend_mode::T::difference => CompositionOp::Difference,
|
||||
mix_blend_mode::T::exclusion => CompositionOp::Exclusion,
|
||||
mix_blend_mode::T::hue => CompositionOp::Hue,
|
||||
mix_blend_mode::T::saturation => CompositionOp::Saturation,
|
||||
mix_blend_mode::T::color => CompositionOp::Color,
|
||||
mix_blend_mode::T::luminosity => CompositionOp::Luminosity,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -371,7 +371,9 @@ impl FragmentDisplayListBuilding for Fragment {
|
|||
// Create the image display item.
|
||||
display_list.push(DisplayItem::ImageClass(box ImageDisplayItem {
|
||||
base: BaseDisplayItem::new(bounds,
|
||||
DisplayItemMetadata::new(self.node, style, Cursor::DefaultCursor),
|
||||
DisplayItemMetadata::new(self.node,
|
||||
style,
|
||||
Cursor::DefaultCursor),
|
||||
clip),
|
||||
image: image.clone(),
|
||||
stretch_size: Size2D(Au::from_px(image.width as int),
|
||||
|
@ -481,7 +483,9 @@ impl FragmentDisplayListBuilding for Fragment {
|
|||
|
||||
let gradient_display_item = DisplayItem::GradientClass(box GradientDisplayItem {
|
||||
base: BaseDisplayItem::new(*absolute_bounds,
|
||||
DisplayItemMetadata::new(self.node, style, Cursor::DefaultCursor),
|
||||
DisplayItemMetadata::new(self.node,
|
||||
style,
|
||||
Cursor::DefaultCursor),
|
||||
clip),
|
||||
start_point: center - delta,
|
||||
end_point: center + delta,
|
||||
|
@ -1194,6 +1198,7 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
|
|||
&overflow,
|
||||
self.fragment.style().get_box().z_index.number_or_zero(),
|
||||
filters,
|
||||
self.fragment.style().get_effects().mix_blend_mode,
|
||||
layer))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,9 +44,9 @@ use std::sync::{Arc, Mutex};
|
|||
use string_cache::Atom;
|
||||
use style::{ComputedValues, TElement, TNode, cascade_anonymous};
|
||||
use style::computed_values::{LengthOrPercentage, LengthOrPercentageOrAuto};
|
||||
use style::computed_values::{LengthOrPercentageOrNone};
|
||||
use style::computed_values::{clear, overflow_wrap, position, text_align, text_decoration};
|
||||
use style::computed_values::{vertical_align, white_space, word_break};
|
||||
use style::computed_values::{LengthOrPercentageOrNone, clear, mix_blend_mode, overflow_wrap};
|
||||
use style::computed_values::{position, text_align, text_decoration, vertical_align, white_space};
|
||||
use style::computed_values::{word_break};
|
||||
use url::Url;
|
||||
|
||||
/// Fragments (`struct Fragment`) are the leaves of the layout tree. They cannot position
|
||||
|
@ -1747,6 +1747,9 @@ impl Fragment {
|
|||
if !self.style().get_effects().filter.is_empty() {
|
||||
return true
|
||||
}
|
||||
if self.style().get_effects().mix_blend_mode != mix_blend_mode::T::normal {
|
||||
return true
|
||||
}
|
||||
match self.style().get_box().position {
|
||||
position::T::absolute | position::T::fixed => {
|
||||
// FIXME(pcwalton): This should only establish a new stacking context when
|
||||
|
|
|
@ -63,7 +63,7 @@ use std::cell::Cell;
|
|||
use std::comm::{channel, Sender, Receiver, Select};
|
||||
use std::mem;
|
||||
use std::ptr;
|
||||
use style::computed_values::filter;
|
||||
use style::computed_values::{filter, mix_blend_mode};
|
||||
use style::{StylesheetOrigin, Stylesheet, Stylist, TNode, iter_font_face_rules};
|
||||
use style::{MediaType, Device};
|
||||
use std::sync::{Arc, Mutex, MutexGuard};
|
||||
|
@ -698,6 +698,7 @@ impl LayoutTask {
|
|||
&origin,
|
||||
0,
|
||||
filter::T::new(Vec::new()),
|
||||
mix_blend_mode::T::normal,
|
||||
Some(paint_layer)));
|
||||
|
||||
rw_data.stacking_context = Some(stacking_context.clone());
|
||||
|
|
|
@ -116,7 +116,9 @@ impl TextRunScanner {
|
|||
let inherited_text_style = in_fragment.style().get_inheritedtext();
|
||||
fontgroup = font_context.get_layout_font_group_for_style(font_style);
|
||||
compression = match in_fragment.white_space() {
|
||||
white_space::T::normal | white_space::T::nowrap => CompressionMode::CompressWhitespaceNewline,
|
||||
white_space::T::normal | white_space::T::nowrap => {
|
||||
CompressionMode::CompressWhitespaceNewline
|
||||
}
|
||||
white_space::T::pre => CompressionMode::CompressNone,
|
||||
};
|
||||
text_transform = inherited_text_style.text_transform;
|
||||
|
@ -129,7 +131,9 @@ impl TextRunScanner {
|
|||
let mut run_text = String::new();
|
||||
for in_fragment in self.clump.iter() {
|
||||
let in_fragment = match in_fragment.specific {
|
||||
SpecificFragmentInfo::UnscannedText(ref text_fragment_info) => &text_fragment_info.text,
|
||||
SpecificFragmentInfo::UnscannedText(ref text_fragment_info) => {
|
||||
&text_fragment_info.text
|
||||
}
|
||||
_ => panic!("Expected an unscanned text fragment!"),
|
||||
};
|
||||
|
||||
|
|
|
@ -1980,6 +1980,9 @@ pub mod longhands {
|
|||
}
|
||||
}
|
||||
</%self:longhand>
|
||||
|
||||
${single_keyword("mix-blend-mode",
|
||||
"normal multiply screen overlay darken lighten color-dodge color-burn hard-light soft-light difference exclusion hue saturation color luminosity")}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -228,3 +228,4 @@ fragment=top != ../html/acid2.html acid2_ref.html
|
|||
== outline_offset_a.html outline_offset_ref.html
|
||||
== filter_opacity_a.html filter_opacity_ref.html
|
||||
== filter_sepia_a.html filter_sepia_ref.html
|
||||
== mix_blend_mode_a.html mix_blend_mode_ref.html
|
||||
|
|
53
tests/ref/mix_blend_mode_a.html
Normal file
53
tests/ref/mix_blend_mode_a.html
Normal file
|
@ -0,0 +1,53 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
html {
|
||||
background: #ffffff;
|
||||
}
|
||||
section {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
}
|
||||
#a {
|
||||
top: 0;
|
||||
mix-blend-mode: difference;
|
||||
}
|
||||
#b {
|
||||
top: 100px;
|
||||
mix-blend-mode: exclusion;
|
||||
}
|
||||
div {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
}
|
||||
.red {
|
||||
left: 0;
|
||||
background-color: #ff0000;
|
||||
}
|
||||
.green {
|
||||
left: 100px;
|
||||
background: #00ff00;
|
||||
}
|
||||
.blue {
|
||||
left: 200px;
|
||||
background: #0000ff;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<section id=a>
|
||||
<div class=red></div>
|
||||
<div class=green></div>
|
||||
<div class=blue></div>
|
||||
</section>
|
||||
<section id=b>
|
||||
<div class=red></div>
|
||||
<div class=green></div>
|
||||
<div class=blue></div>
|
||||
</section>
|
||||
</body>
|
||||
</html>
|
||||
|
52
tests/ref/mix_blend_mode_ref.html
Normal file
52
tests/ref/mix_blend_mode_ref.html
Normal file
|
@ -0,0 +1,52 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
html {
|
||||
background: #ffffff;
|
||||
}
|
||||
section {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
}
|
||||
#a {
|
||||
top: 0;
|
||||
}
|
||||
#b {
|
||||
top: 100px;
|
||||
}
|
||||
div {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
}
|
||||
.red {
|
||||
left: 0;
|
||||
background-color: #00ffff;
|
||||
}
|
||||
.green {
|
||||
left: 100px;
|
||||
background: #ff00ff;
|
||||
}
|
||||
.blue {
|
||||
left: 200px;
|
||||
background: #ffff00;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<section id=a>
|
||||
<div class=red></div>
|
||||
<div class=green></div>
|
||||
<div class=blue></div>
|
||||
</section>
|
||||
<section id=b>
|
||||
<div class=red></div>
|
||||
<div class=green></div>
|
||||
<div class=blue></div>
|
||||
</section>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue