mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
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:
parent
6b3c05cdd2
commit
15d60d7ea4
20 changed files with 499 additions and 62 deletions
|
@ -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>) {
|
||||
|
|
|
@ -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
198
components/gfx/filters.rs
Normal 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,
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
@ -68,6 +68,7 @@ pub mod font_template;
|
|||
|
||||
// Misc.
|
||||
mod buffer_map;
|
||||
mod filters;
|
||||
|
||||
// Platform-specific implementations.
|
||||
#[path="platform/mod.rs"]
|
||||
|
|
|
@ -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)),
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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());
|
||||
|
|
2
components/servo/Cargo.lock
generated
2
components/servo/Cargo.lock
generated
|
@ -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)",
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
2
ports/cef/Cargo.lock
generated
|
@ -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
2
ports/gonk/Cargo.lock
generated
|
@ -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)",
|
||||
|
|
|
@ -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
|
||||
|
|
15
tests/ref/filter_opacity_a.html
Normal file
15
tests/ref/filter_opacity_a.html
Normal 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>
|
||||
|
16
tests/ref/filter_opacity_ref.html
Normal file
16
tests/ref/filter_opacity_ref.html
Normal 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>
|
||||
|
||||
|
18
tests/ref/filter_sepia_a.html
Normal file
18
tests/ref/filter_sepia_a.html
Normal 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>
|
||||
|
17
tests/ref/filter_sepia_ref.html
Normal file
17
tests/ref/filter_sepia_ref.html
Normal 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>
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue