layout: Implement filter per CSS-FILTERS § 5.

`blur` and `drop-shadow` are not yet supported, because the
`text-shadow` PR makes some fundamental changes to blur rendering that
are needed first.
This commit is contained in:
Patrick Walton 2015-01-05 19:22:02 -08:00
parent 6b3c05cdd2
commit 15d60d7ea4
20 changed files with 499 additions and 62 deletions

View file

@ -56,8 +56,7 @@ impl CanvasPaintTask {
fn fill_rect(&self, rect: &Rect<f32>) {
let drawopts = DrawOptions::new(1.0, 0);
self.drawtarget.fill_rect(rect, PatternRef::ColorPatternRef(&self.fill_color),
Some(&drawopts));
self.drawtarget.fill_rect(rect, PatternRef::Color(&self.fill_color), Some(&drawopts));
}
fn clear_rect(&self, rect: &Rect<f32>) {
@ -70,7 +69,7 @@ impl CanvasPaintTask {
}
fn create(size: Size2D<i32>) -> DrawTarget {
DrawTarget::new(BackendType::SkiaBackend, size, SurfaceFormat::B8G8R8A8)
DrawTarget::new(BackendType::Skia, size, SurfaceFormat::B8G8R8A8)
}
fn recreate(&mut self, size: Size2D<i32>) {

View file

@ -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, pointer_events};
use style::computed_values::{border_style, cursor, filter, pointer_events};
// It seems cleaner to have layout code not mention Azure directly, so let's just reexport this for
// layout to use.
@ -163,8 +163,8 @@ pub struct StackingContext {
pub overflow: Rect<Au>,
/// The `z-index` for this stacking context.
pub z_index: i32,
/// The opacity of this stacking context.
pub opacity: AzFloat,
/// CSS filters to be applied to this stacking context (including opacity).
pub filters: filter::T,
}
impl StackingContext {
@ -174,7 +174,7 @@ impl StackingContext {
bounds: &Rect<Au>,
overflow: &Rect<Au>,
z_index: i32,
opacity: AzFloat,
filters: filter::T,
layer: Option<Arc<PaintLayer>>)
-> StackingContext {
StackingContext {
@ -183,7 +183,7 @@ impl StackingContext {
bounds: *bounds,
overflow: *overflow,
z_index: z_index,
opacity: opacity,
filters: filters,
}
}
@ -194,7 +194,7 @@ impl StackingContext {
transform: &Matrix2D<AzFloat>,
clip_rect: Option<&Rect<Au>>) {
let temporary_draw_target =
paint_context.get_or_create_temporary_draw_target(self.opacity);
paint_context.get_or_create_temporary_draw_target(&self.filters);
{
let mut paint_subcontext = PaintContext {
draw_target: temporary_draw_target.clone(),
@ -306,7 +306,8 @@ impl StackingContext {
paint_subcontext.draw_target.set_transform(&old_transform)
}
paint_context.draw_temporary_draw_target_if_necessary(&temporary_draw_target, self.opacity)
paint_context.draw_temporary_draw_target_if_necessary(&temporary_draw_target,
&self.filters)
}
/// Translate the given tile rect into the coordinate system of a child stacking context.

198
components/gfx/filters.rs Normal file
View file

@ -0,0 +1,198 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
//! CSS and SVG filter support.
use azure::AzFloat;
use azure::azure_hl::{ColorMatrixAttribute, ColorMatrixInput, CompositeInput, DrawTarget};
use azure::azure_hl::{FilterNode, FilterType, LinearTransferAttribute, LinearTransferInput};
use azure::azure_hl::{Matrix5x4, TableTransferAttribute, TableTransferInput};
use std::num::FloatMath;
use style::computed_values::filter;
/// Creates a filter pipeline from a set of CSS filters. Returns the destination end of the filter
/// pipeline and the opacity.
pub fn create_filters(draw_target: &DrawTarget,
temporary_draw_target: &DrawTarget,
style_filters: &filter::T)
-> (FilterNode, AzFloat) {
let mut opacity = 1.0;
let mut filter = draw_target.create_filter(FilterType::Composite);
filter.set_input(CompositeInput, &temporary_draw_target.snapshot());
for style_filter in style_filters.filters.iter() {
match *style_filter {
filter::Filter::HueRotate(angle) => {
let hue_rotate = draw_target.create_filter(FilterType::ColorMatrix);
let matrix = self::hue_rotate(angle.radians() as AzFloat);
hue_rotate.set_attribute(ColorMatrixAttribute::Matrix(matrix));
hue_rotate.set_input(ColorMatrixInput, &filter);
filter = hue_rotate
}
filter::Filter::Opacity(opacity_value) => opacity *= opacity_value as AzFloat,
filter::Filter::Saturate(amount) => {
let saturate = draw_target.create_filter(FilterType::ColorMatrix);
let matrix = self::saturate(amount as AzFloat);
saturate.set_attribute(ColorMatrixAttribute::Matrix(matrix));
saturate.set_input(ColorMatrixInput, &filter);
filter = saturate
}
filter::Filter::Sepia(amount) => {
let sepia = draw_target.create_filter(FilterType::ColorMatrix);
let matrix = self::sepia(amount as AzFloat);
sepia.set_attribute(ColorMatrixAttribute::Matrix(matrix));
sepia.set_input(ColorMatrixInput, &filter);
filter = sepia
}
filter::Filter::Grayscale(amount) => {
let amount = amount as AzFloat;
let grayscale = draw_target.create_filter(FilterType::ColorMatrix);
grayscale.set_attribute(ColorMatrixAttribute::Matrix(self::grayscale(amount)));
grayscale.set_input(ColorMatrixInput, &filter);
filter = grayscale
}
filter::Filter::Invert(amount) => {
let amount = amount as AzFloat;
let invert = draw_target.create_filter(FilterType::TableTransfer);
invert.set_attribute(TableTransferAttribute::DisableR(false));
invert.set_attribute(TableTransferAttribute::DisableG(false));
invert.set_attribute(TableTransferAttribute::DisableB(false));
invert.set_attribute(TableTransferAttribute::TableR(&[1.0, amount - 1.0]));
invert.set_attribute(TableTransferAttribute::TableG(&[1.0, amount - 1.0]));
invert.set_attribute(TableTransferAttribute::TableB(&[1.0, amount - 1.0]));
invert.set_input(TableTransferInput, &filter);
filter = invert
}
filter::Filter::Brightness(amount) => {
let amount = amount as AzFloat;
let brightness = draw_target.create_filter(FilterType::LinearTransfer);
brightness.set_attribute(LinearTransferAttribute::DisableR(false));
brightness.set_attribute(LinearTransferAttribute::DisableG(false));
brightness.set_attribute(LinearTransferAttribute::DisableB(false));
brightness.set_attribute(LinearTransferAttribute::SlopeR(amount));
brightness.set_attribute(LinearTransferAttribute::SlopeG(amount));
brightness.set_attribute(LinearTransferAttribute::SlopeB(amount));
brightness.set_input(LinearTransferInput, &filter);
filter = brightness
}
filter::Filter::Contrast(amount) => {
let amount = amount as AzFloat;
let contrast = draw_target.create_filter(FilterType::LinearTransfer);
contrast.set_attribute(LinearTransferAttribute::DisableR(false));
contrast.set_attribute(LinearTransferAttribute::DisableG(false));
contrast.set_attribute(LinearTransferAttribute::DisableB(false));
contrast.set_attribute(LinearTransferAttribute::SlopeR(amount));
contrast.set_attribute(LinearTransferAttribute::SlopeG(amount));
contrast.set_attribute(LinearTransferAttribute::SlopeB(amount));
contrast.set_attribute(LinearTransferAttribute::InterceptR(-0.5 * amount + 0.5));
contrast.set_attribute(LinearTransferAttribute::InterceptG(-0.5 * amount + 0.5));
contrast.set_attribute(LinearTransferAttribute::InterceptB(-0.5 * amount + 0.5));
contrast.set_input(LinearTransferInput, &filter);
filter = contrast
}
}
}
(filter, opacity)
}
/// Determines if we need a temporary draw target for the given set of filters.
pub fn temporary_draw_target_needed_for_style_filters(filters: &filter::T) -> bool {
for filter in filters.filters.iter() {
match *filter {
filter::Filter::Opacity(value) if value == 1.0 => continue,
_ => return true,
}
}
false
}
/// Creates a grayscale 5x4 color matrix per CSS-FILTERS § 12.1.1.
fn grayscale(amount: AzFloat) -> Matrix5x4 {
Matrix5x4 {
m11: 0.2126 + 0.7874 * (1.0 - amount),
m21: 0.7152 - 0.7152 * (1.0 - amount),
m31: 0.0722 - 0.0722 * (1.0 - amount),
m41: 0.0,
m51: 0.0,
m12: 0.2126 - 0.2126 * (1.0 - amount),
m22: 0.7152 + 0.2848 * (1.0 - amount),
m32: 0.0722 - 0.0722 * (1.0 - amount),
m42: 0.0,
m52: 0.0,
m13: 0.2126 - 0.2126 * (1.0 - amount),
m23: 0.7152 - 0.7152 * (1.0 - amount),
m33: 0.0722 + 0.9278 * (1.0 - amount),
m43: 0.0,
m53: 0.0,
m14: 0.0, m24: 0.0, m34: 0.0, m44: 1.0, m54: 0.0,
}
}
/// Creates a 5x4 hue rotation color matrix per CSS-FILTERS § 8.5.
fn hue_rotate(angle: AzFloat) -> Matrix5x4 {
let (c, s) = (angle.cos(), angle.sin());
Matrix5x4 {
m11: 0.213 + c * 0.787 + s * -0.213,
m21: 0.715 + c * -0.715 + s * -0.715,
m31: 0.072 + c * -0.072 + s * 0.928,
m41: 0.0,
m51: 0.0,
m12: 0.213 + c * -0.213 + s * 0.143,
m22: 0.715 + c * 0.285 + s * 0.140,
m32: 0.072 + c * -0.072 + s * -0.283,
m42: 0.0,
m52: 0.0,
m13: 0.213 + c * -0.213 + s * -0.787,
m23: 0.715 + c * -0.715 + s * 0.715,
m33: 0.072 + c * 0.928 + s * 0.072,
m43: 0.0,
m53: 0.0,
m14: 0.0, m24: 0.0, m34: 0.0, m44: 1.0, m54: 0.0,
}
}
/// Creates a 5x4 saturation color matrix per CSS-FILTERS § 8.5.
fn saturate(amount: AzFloat) -> Matrix5x4 {
Matrix5x4 {
m11: 0.213 + 0.787 * amount,
m21: 0.715 - 0.715 * amount,
m31: 0.072 - 0.072 * amount,
m41: 0.0,
m51: 0.0,
m12: 0.213 - 0.213 * amount,
m22: 0.715 + 0.285 * amount,
m32: 0.072 - 0.072 * amount,
m42: 0.0,
m52: 0.0,
m13: 0.213 - 0.213 * amount,
m23: 0.715 - 0.715 * amount,
m33: 0.072 + 0.928 * amount,
m43: 0.0,
m53: 0.0,
m14: 0.0, m24: 0.0, m34: 0.0, m44: 1.0, m54: 0.0,
}
}
/// Creates a sepia 5x4 color matrix per CSS-FILTERS § 12.1.1.
fn sepia(amount: AzFloat) -> Matrix5x4 {
Matrix5x4 {
m11: 0.393 + 0.607 * (1.0 - amount),
m21: 0.769 - 0.769 * (1.0 - amount),
m31: 0.189 - 0.189 * (1.0 - amount),
m41: 0.0,
m51: 0.0,
m12: 0.349 - 0.349 * (1.0 - amount),
m22: 0.686 + 0.314 * (1.0 - amount),
m32: 0.168 - 0.168 * (1.0 - amount),
m42: 0.0,
m52: 0.0,
m13: 0.272 - 0.272 * (1.0 - amount),
m23: 0.534 - 0.534 * (1.0 - amount),
m33: 0.131 + 0.869 * (1.0 - amount),
m43: 0.0,
m53: 0.0,
m14: 0.0, m24: 0.0, m34: 0.0, m44: 1.0, m54: 0.0,
}
}

View file

@ -30,14 +30,14 @@ use azure::scaled_font::FontInfo;
#[cfg(any(target_os="linux", target_os = "android"))]
fn create_scaled_font(template: &Arc<FontTemplateData>, pt_size: Au) -> ScaledFont {
ScaledFont::new(BackendType::SkiaBackend, FontInfo::FontData(&template.bytes),
ScaledFont::new(BackendType::Skia, FontInfo::FontData(&template.bytes),
pt_size.to_subpx() as AzFloat)
}
#[cfg(target_os="macos")]
fn create_scaled_font(template: &Arc<FontTemplateData>, pt_size: Au) -> ScaledFont {
let cgfont = template.ctfont.as_ref().unwrap().copy_to_CGFont();
ScaledFont::new(BackendType::SkiaBackend, &cgfont, pt_size.to_subpx() as AzFloat)
ScaledFont::new(BackendType::Skia, &cgfont, pt_size.to_subpx() as AzFloat)
}
static SMALL_CAPS_SCALE_FACTOR: f64 = 0.8; // Matches FireFox (see gfxFont.h)

View file

@ -68,6 +68,7 @@ pub mod font_template;
// Misc.
mod buffer_map;
mod filters;
// Platform-specific implementations.
#[path="platform/mod.rs"]

View file

@ -5,16 +5,17 @@
//! Painting of display lists using Moz2D/Azure.
use azure::azure::AzIntSize;
use azure::azure_hl::{SurfaceFormat, Color, ColorPattern, DrawOptions};
use azure::azure_hl::{DrawSurfaceOptions, DrawTarget, ExtendMode, FilterType};
use azure::azure_hl::{Color, ColorPattern};
use azure::azure_hl::{DrawOptions, DrawSurfaceOptions, DrawTarget, ExtendMode, FilterType};
use azure::azure_hl::{GaussianBlurInput, GradientStop, Filter, LinearGradientPattern};
use azure::azure_hl::{PatternRef, Path, PathBuilder, CompositionOp};
use azure::azure_hl::{GaussianBlurAttribute, StrokeOptions};
use azure::azure_hl::{GaussianBlurAttribute, StrokeOptions, SurfaceFormat};
use azure::scaled_font::ScaledFont;
use azure::{AZ_CAP_BUTT, AzFloat, struct__AzDrawOptions, struct__AzGlyph};
use azure::{struct__AzGlyphBuffer, struct__AzPoint, AzDrawTargetFillGlyphs};
use display_list::TextOrientation::{SidewaysLeft, SidewaysRight, Upright};
use display_list::{BOX_SHADOW_INFLATION_FACTOR, BorderRadii, ClippingRegion, TextDisplayItem};
use filters;
use font_context::FontContext;
use geom::matrix2d::Matrix2D;
use geom::point::Point2D;
@ -33,7 +34,7 @@ use std::f32;
use std::mem;
use std::num::{Float, FloatMath};
use std::ptr;
use style::computed_values::border_style;
use style::computed_values::{border_style, filter};
use std::sync::Arc;
use text::TextRun;
use text::glyph::CharIndex;
@ -75,7 +76,7 @@ impl<'a> PaintContext<'a> {
pub fn draw_solid_color(&self, bounds: &Rect<Au>, color: Color) {
self.draw_target.make_current();
self.draw_target.fill_rect(&bounds.to_azure_rect(),
PatternRef::ColorPatternRef(&ColorPattern::new(color)),
PatternRef::Color(&ColorPattern::new(color)),
None);
}
@ -158,10 +159,9 @@ impl<'a> PaintContext<'a> {
Size2D(self.screen_rect.size.width as AzFloat,
self.screen_rect.size.height as AzFloat));
let mut draw_options = DrawOptions::new(1.0, 0);
draw_options.set_composition_op(CompositionOp::SourceOp);
draw_options.set_composition_op(CompositionOp::Source);
self.draw_target.make_current();
self.draw_target.fill_rect(&rect, PatternRef::ColorPatternRef(&pattern),
Some(&draw_options));
self.draw_target.fill_rect(&rect, PatternRef::Color(&pattern), Some(&draw_options));
}
fn draw_border_segment(&self,
@ -859,19 +859,20 @@ impl<'a> PaintContext<'a> {
stops: &[GradientStop]) {
self.draw_target.make_current();
let stops = self.draw_target.create_gradient_stops(stops, ExtendMode::ExtendClamp);
let stops = self.draw_target.create_gradient_stops(stops, ExtendMode::Clamp);
let pattern = LinearGradientPattern::new(&start_point.to_azure_point(),
&end_point.to_azure_point(),
stops,
&Matrix2D::identity());
self.draw_target.fill_rect(&bounds.to_azure_rect(),
PatternRef::LinearGradientPatternRef(&pattern),
PatternRef::LinearGradient(&pattern),
None);
}
pub fn get_or_create_temporary_draw_target(&mut self, opacity: AzFloat) -> DrawTarget {
if opacity == 1.0 {
pub fn get_or_create_temporary_draw_target(&mut self, filters: &filter::T) -> DrawTarget {
// Determine if we need a temporary draw target.
if !filters::temporary_draw_target_needed_for_style_filters(filters) {
// 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,
@ -883,10 +884,7 @@ impl<'a> PaintContext<'a> {
// FIXME(pcwalton): This surface might be bigger than necessary and waste memory.
let size = self.draw_target.get_size();
let size = Size2D {
width: size.width,
height: size.height,
};
let size = Size2D(size.width, size.height);
let temporary_draw_target =
self.draw_target.create_similar_draw_target(&size, self.draw_target.get_format());
@ -898,24 +896,29 @@ 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,
opacity: AzFloat) {
filters: &filter::T) {
if (*temporary_draw_target) == self.draw_target {
// We're directly painting to the surface; nothing to do.
return
}
// Set up transforms.
let old_transform = self.draw_target.get_transform();
self.draw_target.set_transform(&Matrix2D::identity());
temporary_draw_target.set_transform(&Matrix2D::identity());
// Create the Azure filter pipeline.
let (filter_node, opacity) = filters::create_filters(&self.draw_target,
temporary_draw_target,
filters);
// Perform the blit operation.
let rect = Rect(Point2D(0.0, 0.0), self.draw_target.get_size().to_azure_size());
let source_surface = temporary_draw_target.snapshot();
let draw_surface_options = DrawSurfaceOptions::new(Filter::Linear, true);
let draw_options = DrawOptions::new(opacity, 0);
self.draw_target.draw_surface(source_surface,
rect,
rect,
draw_surface_options,
draw_options);
self.draw_target.draw_filter(&filter_node,
&rect,
&rect.origin,
draw_options);
self.draw_target.set_transform(&old_transform);
}
@ -974,9 +977,9 @@ impl<'a> PaintContext<'a> {
if blur_radius > Au(0) {
// Go ahead and create the blur now. Despite the name, Azure's notion of `StdDeviation`
// describes the blur radius, not the sigma for the Gaussian blur.
let blur_filter = self.draw_target.create_filter(FilterType::GaussianBlurFilterType);
blur_filter.set_attribute(GaussianBlurAttribute::StdDeviationGaussianBlurAttribute(
blur_radius.to_subpx() as AzFloat));
let blur_filter = self.draw_target.create_filter(FilterType::GaussianBlur);
blur_filter.set_attribute(GaussianBlurAttribute::StdDeviation(blur_radius.to_subpx() as
AzFloat));
blur_filter.set_input(GaussianBlurInput, &temporary_draw_target.snapshot());
// Blit the blur onto the tile. We undo the transforms here because we want to directly
@ -985,7 +988,7 @@ impl<'a> PaintContext<'a> {
self.draw_target.set_transform(&Matrix2D::identity());
let temporary_draw_target_size = temporary_draw_target.get_size();
self.draw_target
.draw_filter(blur_filter,
.draw_filter(&blur_filter,
&Rect(Point2D(0.0, 0.0),
Size2D(temporary_draw_target_size.width as AzFloat,
temporary_draw_target_size.height as AzFloat)),

View file

@ -524,11 +524,11 @@ impl WorkerThread {
-> DrawTarget {
let size = Size2D(tile.screen_rect.size.width as i32, tile.screen_rect.size.height as i32);
let draw_target = if !opts::get().gpu_painting {
DrawTarget::new(BackendType::SkiaBackend, size, SurfaceFormat::B8G8R8A8)
DrawTarget::new(BackendType::Skia, size, SurfaceFormat::B8G8R8A8)
} else {
// FIXME(pcwalton): Cache the components of draw targets (texture color buffer,
// paintbuffers) instead of recreating them.
let draw_target = DrawTarget::new_with_fbo(BackendType::SkiaBackend,
let draw_target = DrawTarget::new_with_fbo(BackendType::Skia,
native_graphics_context!(self),
size,
SurfaceFormat::B8G8R8A8);

View file

@ -44,6 +44,7 @@ use std::default::Default;
use std::num::FloatMath;
use style::computed::{AngleOrCorner, LengthOrPercentage, HorizontalDirection, VerticalDirection};
use style::computed::{Image, LinearGradient};
use style::computed_values::filter::Filter;
use style::computed_values::{background_attachment, background_repeat, border_style, overflow};
use style::computed_values::{position, visibility};
use style::style_structs::Border;
@ -1147,11 +1148,18 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
let margin = self.fragment.margin.to_physical(self.base.writing_mode);
let overflow = self.base.overflow.translate(&-Point2D(margin.left, Au(0)));
// Create the filter pipeline.
let effects = self.fragment.style().get_effects();
let mut filters = effects.filter.clone();
if effects.opacity != 1.0 {
filters.push(Filter::Opacity(effects.opacity))
}
Arc::new(StackingContext::new(display_list,
&border_box,
&overflow,
self.fragment.style().get_box().z_index.number_or_zero(),
self.fragment.style().get_effects().opacity as f32,
filters,
layer))
}
}

View file

@ -1623,6 +1623,9 @@ impl Fragment {
if self.style().get_effects().opacity != 1.0 {
return true
}
if !self.style().get_effects().filter.is_empty() {
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

View file

@ -45,8 +45,7 @@ use script_traits::{ConstellationControlMsg, CompositorEvent, OpaqueScriptLayout
use script_traits::{ScriptControlChan, UntrustedNodeAddress};
use servo_msg::compositor_msg::ScrollPolicy;
use servo_msg::constellation_msg::Msg as ConstellationMsg;
use servo_msg::constellation_msg::{ConstellationChan, Failure, PipelineExitType};
use servo_msg::constellation_msg::PipelineId;
use servo_msg::constellation_msg::{ConstellationChan, Failure, PipelineExitType, PipelineId};
use servo_net::image_cache_task::{ImageCacheTask, ImageResponseMsg};
use servo_net::local_image_cache::{ImageResponder, LocalImageCache};
use servo_net::resource_task::{ResourceTask, load_bytes_iter};
@ -57,13 +56,14 @@ use servo_util::opts;
use servo_util::smallvec::{SmallVec, SmallVec1, VecLike};
use servo_util::task::spawn_named_with_send_on_failure;
use servo_util::task_state;
use servo_util::time::{TimeProfilerCategory, ProfilerMetadata, TimeProfilerChan, TimerMetadataFrameType};
use servo_util::time::{TimerMetadataReflowType, profile};
use servo_util::time::{TimeProfilerCategory, ProfilerMetadata, TimeProfilerChan};
use servo_util::time::{TimerMetadataFrameType, TimerMetadataReflowType, profile};
use servo_util::workqueue::WorkQueue;
use std::cell::Cell;
use std::comm::{channel, Sender, Receiver, Select};
use std::mem;
use std::ptr;
use style::computed_values::filter;
use style::{StylesheetOrigin, Stylesheet, Stylist, TNode, iter_font_face_rules};
use style::{MediaType, Device};
use std::sync::{Arc, Mutex, MutexGuard};
@ -697,7 +697,7 @@ impl LayoutTask {
&origin,
&origin,
0,
1.0,
filter::T::new(Vec::new()),
Some(paint_layer)));
rw_data.stacking_context = Some(stacking_context.clone());

View file

@ -26,7 +26,7 @@ dependencies = [
[[package]]
name = "azure"
version = "0.1.0"
source = "git+https://github.com/servo/rust-azure#6884d442052becfcafccc82b17ab316c7831d8d2"
source = "git+https://github.com/servo/rust-azure#e1a91efa633f155da89c6a1dadb0049d847b319d"
dependencies = [
"core_foundation 0.1.0 (git+https://github.com/servo/rust-core-foundation)",
"core_graphics 0.1.0 (git+https://github.com/servo/rust-core-graphics)",

View file

@ -434,6 +434,15 @@ pub mod specified {
Err(())
}
}
/// Parses an angle from a token according to CSS-VALUES § 6.1.
pub fn parse(value: &ComponentValue) -> Result<Angle,()> {
match *value {
Dimension(ref value, ref unit) => {
Angle::parse_dimension(value.value, unit.as_slice())
}
_ => Err(())
}
}
}
/// Specified values for an image according to CSS-IMAGES.

View file

@ -1663,19 +1663,24 @@ pub mod longhands {
pub fn to_computed_value(value: SpecifiedValue, context: &computed::Context)
-> computed_value::T {
value.into_iter().map(|value| {
computed_value::BoxShadow {
offset_x: computed::compute_Au(value.offset_x, context),
offset_y: computed::compute_Au(value.offset_y, context),
blur_radius: computed::compute_Au(value.blur_radius, context),
spread_radius: computed::compute_Au(value.spread_radius, context),
color: value.color.map(|color| color.parsed).unwrap_or(cssparser::Color::CurrentColor),
inset: value.inset,
}
}).collect()
value.into_iter().map(|value| compute_one_box_shadow(value, context)).collect()
}
fn parse_one_box_shadow(iter: ParserIter) -> Result<SpecifiedBoxShadow,()> {
pub fn compute_one_box_shadow(value: SpecifiedBoxShadow, context: &computed::Context)
-> computed_value::BoxShadow {
computed_value::BoxShadow {
offset_x: computed::compute_Au(value.offset_x, context),
offset_y: computed::compute_Au(value.offset_y, context),
blur_radius: computed::compute_Au(value.blur_radius, context),
spread_radius: computed::compute_Au(value.spread_radius, context),
color: value.color
.map(|color| color.parsed)
.unwrap_or(cssparser::Color::CurrentColor),
inset: value.inset,
}
}
pub fn parse_one_box_shadow(iter: ParserIter) -> Result<SpecifiedBoxShadow,()> {
let mut lengths = [specified::Length::Au(Au(0)), ..4];
let mut lengths_parsed = false;
let mut color = None;
@ -1833,6 +1838,148 @@ pub mod longhands {
}
}
</%self:single_component_value>
<%self:longhand name="filter">
pub mod computed_value {
use super::super::{Angle, CSSFloat};
// TODO(pcwalton): `blur`, `drop-shadow`
#[deriving(Clone, PartialEq, Show)]
pub enum Filter {
Brightness(CSSFloat),
Contrast(CSSFloat),
Grayscale(CSSFloat),
HueRotate(Angle),
Invert(CSSFloat),
Opacity(CSSFloat),
Saturate(CSSFloat),
Sepia(CSSFloat),
}
#[deriving(Clone, PartialEq, Show)]
pub struct T {
pub filters: Vec<Filter>,
}
impl T {
/// Creates a new filter pipeline.
#[inline]
pub fn new(filters: Vec<Filter>) -> T {
T {
filters: filters,
}
}
/// Adds a new filter to the filter pipeline.
#[inline]
pub fn push(&mut self, filter: Filter) {
self.filters.push(filter)
}
/// Returns true if this filter pipeline is empty and false otherwise.
#[inline]
pub fn is_empty(&self) -> bool {
self.filters.is_empty()
}
/// Returns the resulting opacity of this filter pipeline.
#[inline]
pub fn opacity(&self) -> CSSFloat {
let mut opacity = 1.0;
for filter in self.filters.iter() {
if let Filter::Opacity(ref opacity_value) = *filter {
opacity *= *opacity_value
}
}
opacity
}
}
}
// TODO(pcwalton): `blur`, `drop-shadow`
#[deriving(Clone, Show)]
pub enum SpecifiedFilter {
Brightness(CSSFloat),
Contrast(CSSFloat),
Grayscale(CSSFloat),
HueRotate(Angle),
Invert(CSSFloat),
Opacity(CSSFloat),
Saturate(CSSFloat),
Sepia(CSSFloat),
}
pub type SpecifiedValue = Vec<SpecifiedFilter>;
#[inline]
pub fn get_initial_value() -> computed_value::T {
computed_value::T::new(Vec::new())
}
pub fn to_computed_value(value: SpecifiedValue, _: &computed::Context)
-> computed_value::T {
computed_value::T::new(value.into_iter().map(|filter| {
match filter {
SpecifiedFilter::Brightness(amount) => {
computed_value::Filter::Brightness(amount)
}
SpecifiedFilter::Contrast(amount) => computed_value::Filter::Contrast(amount),
SpecifiedFilter::Grayscale(amount) => {
computed_value::Filter::Grayscale(amount)
}
SpecifiedFilter::HueRotate(angle) => computed_value::Filter::HueRotate(angle),
SpecifiedFilter::Invert(amount) => computed_value::Filter::Invert(amount),
SpecifiedFilter::Opacity(amount) => computed_value::Filter::Opacity(amount),
SpecifiedFilter::Saturate(amount) => computed_value::Filter::Saturate(amount),
SpecifiedFilter::Sepia(amount) => computed_value::Filter::Sepia(amount),
}
}).collect())
}
pub fn parse(input: &[ComponentValue], _: &Url) -> Result<SpecifiedValue,()> {
let mut filters = Vec::new();
for filter in input.skip_whitespace() {
let name;
let args;
match *filter {
Function(ref function_name, ref function_args) => {
name = function_name;
args = function_args;
}
_ => return Err(()),
}
if name.eq_ignore_ascii_case("brightness") && args.len() == 1 {
filters.push(SpecifiedFilter::Brightness(try!(parse_percentage(&args[0]))));
} else if name.eq_ignore_ascii_case("contrast") && args.len() == 1 {
filters.push(SpecifiedFilter::Contrast(try!(parse_percentage(&args[0]))));
} else if name.eq_ignore_ascii_case("grayscale") && args.len() == 1 {
filters.push(SpecifiedFilter::Grayscale(try!(parse_percentage(&args[0]))));
} else if name.eq_ignore_ascii_case("hue-rotate") && args.len() == 1 {
filters.push(SpecifiedFilter::HueRotate(try!(Angle::parse(&args[0]))));
} else if name.eq_ignore_ascii_case("invert") && args.len() == 1 {
filters.push(SpecifiedFilter::Invert(try!(parse_percentage(&args[0]))));
} else if name.eq_ignore_ascii_case("opacity") && args.len() == 1 {
filters.push(SpecifiedFilter::Opacity(try!(parse_percentage(&args[0]))));
} else if name.eq_ignore_ascii_case("saturate") && args.len() == 1 {
filters.push(SpecifiedFilter::Saturate(try!(parse_percentage(&args[0]))));
} else if name.eq_ignore_ascii_case("sepia") && args.len() == 1 {
filters.push(SpecifiedFilter::Sepia(try!(parse_percentage(&args[0]))));
} else {
return Err(())
}
}
Ok(filters)
}
fn parse_percentage(input: &ComponentValue) -> Result<CSSFloat,()> {
match *input {
Number(ref value) => Ok(value.value),
Percentage(ref value) => Ok(value.value / 100.0),
_ => Err(())
}
}
</%self:longhand>
}

2
ports/cef/Cargo.lock generated
View file

@ -27,7 +27,7 @@ dependencies = [
[[package]]
name = "azure"
version = "0.1.0"
source = "git+https://github.com/servo/rust-azure#6884d442052becfcafccc82b17ab316c7831d8d2"
source = "git+https://github.com/servo/rust-azure#e1a91efa633f155da89c6a1dadb0049d847b319d"
dependencies = [
"core_foundation 0.1.0 (git+https://github.com/servo/rust-core-foundation)",
"core_graphics 0.1.0 (git+https://github.com/servo/rust-core-graphics)",

2
ports/gonk/Cargo.lock generated
View file

@ -15,7 +15,7 @@ dependencies = [
[[package]]
name = "azure"
version = "0.1.0"
source = "git+https://github.com/servo/rust-azure#6884d442052becfcafccc82b17ab316c7831d8d2"
source = "git+https://github.com/servo/rust-azure#e1a91efa633f155da89c6a1dadb0049d847b319d"
dependencies = [
"core_foundation 0.1.0 (git+https://github.com/servo/rust-core-foundation)",
"core_graphics 0.1.0 (git+https://github.com/servo/rust-core-graphics)",

View file

@ -226,3 +226,5 @@ fragment=top != ../html/acid2.html acid2_ref.html
== stacking_context_overflow_relative_outline_a.html stacking_context_overflow_relative_outline_ref.html
== word_break_a.html word_break_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

View file

@ -0,0 +1,15 @@
<!DOCTYPE html>
<html>
<head>
<style>
img {
filter: opacity(0.5);
display: block;
}
</style>
</head>
<body>
<img src=test.jpeg>
</body>
</html>

View file

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html>
<head>
<style>
img {
opacity: 0.5;
display: block;
}
</style>
</head>
<body>
<img src=test.jpeg>
</body>
</html>

View file

@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<style>
section {
filter: sepia(100%);
display: block;
width: 100px;
height: 100px;
background: #ff00ff;
}
</style>
</head>
<body>
<section></section>
</body>
</html>

View file

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html>
<head>
<style>
section {
display: block;
width: 100px;
height: 100px;
background: #938567;
}
</style>
</head>
<body>
<section></section>
</body>
</html>