First step towards 3d transforms.

* Add parser support for 3d transforms.
 * Change ComputedMatrix to a representation that suits interpolation.
 * Switch stacking contexts to use 4x4 matrices.

The transforms themselves are still converted to 2d and handled by azure for now, but this is a small standalone part that can be landed now to make it easier to review.
This commit is contained in:
Glenn Watson 2015-05-29 08:31:05 +10:00
parent 09f2977cc9
commit f47ba6fd33
12 changed files with 530 additions and 186 deletions

View file

@ -27,7 +27,7 @@ use azure::azure::AzFloat;
use azure::azure_hl::{Color}; use azure::azure_hl::{Color};
use collections::linked_list::{self, LinkedList}; use collections::linked_list::{self, LinkedList};
use geom::{Point2D, Rect, SideOffsets2D, Size2D, Matrix2D}; use geom::{Matrix2D, Point2D, Rect, SideOffsets2D, Size2D, Matrix4};
use geom::approxeq::ApproxEq; use geom::approxeq::ApproxEq;
use geom::num::Zero; use geom::num::Zero;
use libc::uintptr_t; use libc::uintptr_t;
@ -254,9 +254,7 @@ pub struct StackingContext {
pub blend_mode: mix_blend_mode::T, pub blend_mode: mix_blend_mode::T,
/// A transform to be applied to this stacking context. /// A transform to be applied to this stacking context.
/// pub transform: Matrix4<AzFloat>,
/// TODO(pcwalton): 3D transforms.
pub transform: Matrix2D<AzFloat>,
} }
impl StackingContext { impl StackingContext {
@ -266,7 +264,7 @@ impl StackingContext {
bounds: &Rect<Au>, bounds: &Rect<Au>,
overflow: &Rect<Au>, overflow: &Rect<Au>,
z_index: i32, z_index: i32,
transform: &Matrix2D<AzFloat>, transform: &Matrix4<AzFloat>,
filters: filter::T, filters: filter::T,
blend_mode: mix_blend_mode::T, blend_mode: mix_blend_mode::T,
layer: Option<Arc<PaintLayer>>) layer: Option<Arc<PaintLayer>>)
@ -287,7 +285,7 @@ impl StackingContext {
pub fn optimize_and_draw_into_context(&self, pub fn optimize_and_draw_into_context(&self,
paint_context: &mut PaintContext, paint_context: &mut PaintContext,
tile_bounds: &Rect<AzFloat>, tile_bounds: &Rect<AzFloat>,
transform: &Matrix2D<AzFloat>, transform: &Matrix4<AzFloat>,
clip_rect: Option<&Rect<Au>>) { clip_rect: Option<&Rect<Au>>) {
let transform = transform.mul(&self.transform); let transform = transform.mul(&self.transform);
let temporary_draw_target = let temporary_draw_target =
@ -320,7 +318,10 @@ impl StackingContext {
// Set up our clip rect and transform. // Set up our clip rect and transform.
let old_transform = paint_subcontext.draw_target.get_transform(); let old_transform = paint_subcontext.draw_target.get_transform();
paint_subcontext.draw_target.set_transform(&transform); let xform_2d = Matrix2D::new(transform.m11, transform.m12,
transform.m21, transform.m22,
transform.m41, transform.m42);
paint_subcontext.draw_target.set_transform(&xform_2d);
paint_subcontext.push_clip_if_applicable(); paint_subcontext.push_clip_if_applicable();
// Steps 1 and 2: Borders and background for the root. // Steps 1 and 2: Borders and background for the root.
@ -342,7 +343,8 @@ impl StackingContext {
positioned_kid.bounds positioned_kid.bounds
.origin .origin
.y .y
.to_nearest_px() as AzFloat); .to_nearest_px() as AzFloat,
0.0);
let new_tile_rect = let new_tile_rect =
self.compute_tile_rect_for_child_stacking_context(tile_bounds, self.compute_tile_rect_for_child_stacking_context(tile_bounds,
&**positioned_kid); &**positioned_kid);
@ -390,7 +392,8 @@ impl StackingContext {
positioned_kid.bounds positioned_kid.bounds
.origin .origin
.y .y
.to_nearest_px() as AzFloat); .to_nearest_px() as AzFloat,
0.0);
let new_tile_rect = let new_tile_rect =
self.compute_tile_rect_for_child_stacking_context(tile_bounds, self.compute_tile_rect_for_child_stacking_context(tile_bounds,
&**positioned_kid); &**positioned_kid);

View file

@ -12,7 +12,8 @@ use paint_context::PaintContext;
use azure::azure_hl::{SurfaceFormat, Color, DrawTarget, BackendType}; use azure::azure_hl::{SurfaceFormat, Color, DrawTarget, BackendType};
use azure::AzFloat; use azure::AzFloat;
use geom::matrix2d::Matrix2D; use geom::Matrix4;
use geom::matrix::identity;
use geom::point::Point2D; use geom::point::Point2D;
use geom::rect::Rect; use geom::rect::Rect;
use geom::size::Size2D; use geom::size::Size2D;
@ -579,10 +580,11 @@ impl WorkerThread {
stacking_context.overflow.origin.y.to_f32_px())); stacking_context.overflow.origin.y.to_f32_px()));
// Apply the translation to paint the tile we want. // Apply the translation to paint the tile we want.
let matrix: Matrix2D<AzFloat> = Matrix2D::identity(); let matrix: Matrix4<AzFloat> = identity();
let matrix = matrix.scale(scale as AzFloat, scale as AzFloat); let matrix = matrix.scale(scale as AzFloat, scale as AzFloat, 1.0);
let matrix = matrix.translate(-tile_bounds.origin.x as AzFloat, let matrix = matrix.translate(-tile_bounds.origin.x as AzFloat,
-tile_bounds.origin.y as AzFloat); -tile_bounds.origin.y as AzFloat,
0.0);
// Clear the buffer. // Clear the buffer.
paint_context.clear(); paint_context.clear();

View file

@ -18,10 +18,12 @@ use fragment::{CoordinateSystem, Fragment, IframeFragmentInfo, ImageFragmentInfo
use fragment::{ScannedTextFragmentInfo, SpecificFragmentInfo}; use fragment::{ScannedTextFragmentInfo, SpecificFragmentInfo};
use inline::InlineFlow; use inline::InlineFlow;
use list_item::ListItemFlow; use list_item::ListItemFlow;
use model::{self, MaybeAuto, ToGfxMatrix}; use model::{self, MaybeAuto, ToGfxMatrix, ToAu};
use table_cell::CollapsedBordersForCell; use table_cell::CollapsedBordersForCell;
use geom::{Matrix2D, Point2D, Rect, Size2D, SideOffsets2D}; use geom::{Point2D, Rect, Size2D, SideOffsets2D};
use geom::matrix::identity;
use geom::Matrix4;
use gfx_traits::color; use gfx_traits::color;
use gfx::display_list::{BLUR_INFLATION_FACTOR, BaseDisplayItem, BorderDisplayItem}; use gfx::display_list::{BLUR_INFLATION_FACTOR, BaseDisplayItem, BorderDisplayItem};
use gfx::display_list::{BorderRadii, BoxShadowClipMode, BoxShadowDisplayItem, ClippingRegion}; use gfx::display_list::{BorderRadii, BoxShadowClipMode, BoxShadowDisplayItem, ClippingRegion};
@ -40,11 +42,12 @@ use std::cmp;
use std::default::Default; use std::default::Default;
use std::iter::repeat; use std::iter::repeat;
use std::sync::Arc; use std::sync::Arc;
use std::f32;
use style::computed_values::filter::Filter; use style::computed_values::filter::Filter;
use style::computed_values::transform::ComputedMatrix;
use style::computed_values::{background_attachment, background_clip, background_origin, use style::computed_values::{background_attachment, background_clip, background_origin,
background_repeat, background_size}; background_repeat, background_size};
use style::computed_values::{border_style, image_rendering, overflow_x, position, visibility}; use style::computed_values::{border_style, image_rendering, overflow_x, position,
visibility, transform};
use style::properties::ComputedValues; use style::properties::ComputedValues;
use style::properties::style_structs::Border; use style::properties::style_structs::Border;
use style::values::RGBA; use style::values::RGBA;
@ -1139,17 +1142,57 @@ impl FragmentDisplayListBuilding for Fragment {
.relative_containing_block_mode, .relative_containing_block_mode,
CoordinateSystem::Parent); CoordinateSystem::Parent);
let transform_origin = self.style().get_effects().transform_origin; let mut transform = identity();
let transform_origin =
Point2D(model::specified(transform_origin.horizontal,
border_box.size.width).to_f32_px(),
model::specified(transform_origin.vertical,
border_box.size.height).to_f32_px());
let transform = self.style().get_effects().transform
.unwrap_or(ComputedMatrix::identity()).to_gfx_matrix(&border_box.size);
let transform = Matrix2D::identity().translate(transform_origin.x, transform_origin.y) if let Some(ref operations) = self.style().get_effects().transform {
.mul(&transform).translate(-transform_origin.x, -transform_origin.y); let transform_origin = self.style().get_effects().transform_origin;
let transform_origin =
Point2D(model::specified(transform_origin.horizontal,
border_box.size.width).to_f32_px(),
model::specified(transform_origin.vertical,
border_box.size.height).to_f32_px());
let pre_transform = Matrix4::create_translation(transform_origin.x,
transform_origin.y,
0.0);
let post_transform = Matrix4::create_translation(-transform_origin.x,
-transform_origin.y,
0.0);
for operation in operations {
let matrix = match operation {
&transform::ComputedOperation::Rotate(ax, ay, az, theta) => {
let theta = f32::consts::PI_2 - theta.radians();
let transform = Matrix4::create_rotation(ax, ay, az, theta);
pre_transform.mul(&transform).mul(&post_transform)
}
&transform::ComputedOperation::Perspective(d) => {
let transform = Matrix4::create_perspective(d.to_f32_px());
pre_transform.mul(&transform).mul(&post_transform)
}
&transform::ComputedOperation::Scale(sx, sy, sz) => {
let transform = Matrix4::create_scale(sx, sy, sz);
pre_transform.mul(&transform).mul(&post_transform)
}
&transform::ComputedOperation::Translate(tx, ty, tz) => {
let tx = tx.to_au(border_box.size.width).to_f32_px();
let ty = ty.to_au(border_box.size.height).to_f32_px();
let tz = tz.to_f32_px();
Matrix4::create_translation(tx, ty, tz)
}
&transform::ComputedOperation::Matrix(m) => {
let transform = m.to_gfx_matrix(&border_box.size);
pre_transform.mul(&transform).mul(&post_transform)
}
&transform::ComputedOperation::Skew(sx, sy) => {
let transform = Matrix4::create_skew(sx, sy);
pre_transform.mul(&transform).mul(&post_transform)
}
};
transform = transform.mul(&matrix);
}
}
// FIXME(pcwalton): Is this vertical-writing-direction-safe? // FIXME(pcwalton): Is this vertical-writing-direction-safe?
let margin = self.margin.to_physical(base_flow.writing_mode); let margin = self.margin.to_physical(base_flow.writing_mode);

View file

@ -27,7 +27,7 @@ use azure::azure::AzColor;
use canvas_traits::CanvasMsg; use canvas_traits::CanvasMsg;
use encoding::EncodingRef; use encoding::EncodingRef;
use encoding::all::UTF_8; use encoding::all::UTF_8;
use geom::matrix2d::Matrix2D; use geom::matrix;
use geom::point::Point2D; use geom::point::Point2D;
use geom::rect::Rect; use geom::rect::Rect;
use geom::scale_factor::ScaleFactor; use geom::scale_factor::ScaleFactor;
@ -854,7 +854,7 @@ impl LayoutTask {
&origin, &origin,
&origin, &origin,
0, 0,
&Matrix2D::identity(), &matrix::identity(),
filter::T::new(Vec::new()), filter::T::new(Vec::new()),
mix_blend_mode::T::normal, mix_blend_mode::T::normal,
Some(paint_layer))); Some(paint_layer)));

View file

@ -8,7 +8,7 @@
use fragment::Fragment; use fragment::Fragment;
use geom::{Matrix2D, SideOffsets2D, Size2D}; use geom::{Matrix4, SideOffsets2D, Size2D};
use std::cmp::{max, min}; use std::cmp::{max, min};
use std::fmt; use std::fmt;
use style::computed_values::transform::ComputedMatrix; use style::computed_values::transform::ComputedMatrix;
@ -426,21 +426,24 @@ pub fn padding_from_style(style: &ComputedValues, containing_block_inline_size:
} }
pub trait ToGfxMatrix { pub trait ToGfxMatrix {
fn to_gfx_matrix(&self, containing_size: &Size2D<Au>) -> Matrix2D<f32>; fn to_gfx_matrix(&self, containing_size: &Size2D<Au>) -> Matrix4<f32>;
} }
impl ToGfxMatrix for ComputedMatrix { impl ToGfxMatrix for ComputedMatrix {
fn to_gfx_matrix(&self, containing_size: &Size2D<Au>) -> Matrix2D<f32> { fn to_gfx_matrix(&self, containing_size: &Size2D<Au>) -> Matrix4<f32> {
Matrix2D::new(self.m11 as f32, Matrix4 {
self.m12 as f32, m11: self.m11 as f32, m12: self.m12 as f32, m13: self.m13 as f32, m14: self.m14 as f32,
self.m21 as f32, m21: self.m21 as f32, m22: self.m22 as f32, m23: self.m23 as f32, m24: self.m24 as f32,
self.m22 as f32, m31: self.m31 as f32, m32: self.m32 as f32, m33: self.m33 as f32, m34: self.m34 as f32,
self.m31.to_au(containing_size.width).to_f32_px(), m41: self.m41.to_au(containing_size.width).to_f32_px(),
self.m32.to_au(containing_size.height).to_f32_px()) m42: self.m42.to_au(containing_size.height).to_f32_px(),
m43: self.m43 as f32,
m44: self.m44 as f32
}
} }
} }
trait ToAu { pub trait ToAu {
fn to_au(&self, containing_size: Au) -> Au; fn to_au(&self, containing_size: Au) -> Au;
} }

View file

@ -357,7 +357,7 @@ dependencies = [
[[package]] [[package]]
name = "geom" name = "geom"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/servo/rust-geom#c4bdb1ef8f4915ae636eb752b103f69246b50304" source = "git+https://github.com/servo/rust-geom#270d0246b79fbf86fc2938c4952cae74e4025fcf"
dependencies = [ dependencies = [
"log 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"num 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "num 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)",

View file

@ -3100,104 +3100,49 @@ pub mod longhands {
use values::computed::{ToComputedValue, Context}; use values::computed::{ToComputedValue, Context};
use cssparser::ToCss; use cssparser::ToCss;
use std::f32;
use std::ops::Mul;
use std::fmt; use std::fmt;
use util::geometry::Au; use util::geometry::Au;
pub mod computed_value { pub mod computed_value {
use values::CSSFloat; use values::CSSFloat;
use values::computed; use values::computed;
use values::specified;
use std::ops::Mul;
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
pub struct ComputedMatrix { pub struct ComputedMatrix {
pub m11: CSSFloat, pub m12: CSSFloat, pub m11: CSSFloat, pub m12: CSSFloat, pub m13: CSSFloat, pub m14: CSSFloat,
pub m21: CSSFloat, pub m22: CSSFloat, pub m21: CSSFloat, pub m22: CSSFloat, pub m23: CSSFloat, pub m24: CSSFloat,
pub m31: computed::LengthAndPercentage, pub m32: computed::LengthAndPercentage, pub m31: CSSFloat, pub m32: CSSFloat, pub m33: CSSFloat, pub m34: CSSFloat,
pub m41: computed::LengthAndPercentage,
pub m42: computed::LengthAndPercentage,
pub m43: CSSFloat,
pub m44: CSSFloat,
} }
impl Mul<ComputedMatrix> for ComputedMatrix { #[derive(Clone, Debug, PartialEq)]
type Output = ComputedMatrix; pub enum ComputedOperation {
Matrix(ComputedMatrix),
fn mul(self, other: ComputedMatrix) -> ComputedMatrix { Skew(CSSFloat, CSSFloat),
ComputedMatrix { Translate(computed::LengthAndPercentage,
m11: self.m11*other.m11 + self.m12*other.m21, computed::LengthAndPercentage,
m12: self.m11*other.m12 + self.m12*other.m22, computed::Length),
m21: self.m21*other.m11 + self.m22*other.m21, Scale(CSSFloat, CSSFloat, CSSFloat),
m22: self.m21*other.m12 + self.m22*other.m22, Rotate(CSSFloat, CSSFloat, CSSFloat, specified::Angle),
m31: self.m31.clone()*other.m11 + self.m32.clone()*other.m21 + other.m31, Perspective(computed::Length),
m32: self.m31*other.m12 + self.m32*other.m22 + other.m32,
}
}
} }
impl ComputedMatrix { pub type T = Option<Vec<ComputedOperation>>;
#[inline]
fn new(m11: CSSFloat,
m12: CSSFloat,
m21: CSSFloat,
m22: CSSFloat,
m31: computed::LengthAndPercentage,
m32: computed::LengthAndPercentage)
-> ComputedMatrix {
ComputedMatrix {
m11: m11, m12: m12,
m21: m21, m22: m22,
m31: m31, m32: m32,
}
}
#[inline]
pub fn identity() -> ComputedMatrix {
ComputedMatrix {
m11: 1.0, m12: 0.0,
m21: 0.0, m22: 1.0,
m31: computed::LengthAndPercentage::zero(),
m32: computed::LengthAndPercentage::zero(),
}
}
pub fn scale(&mut self, sx: CSSFloat, sy: CSSFloat) {
*self = ComputedMatrix::new(sx, 0.0,
0.0, sy,
computed::LengthAndPercentage::zero(),
computed::LengthAndPercentage::zero()) *
(*self).clone()
}
pub fn skew(&mut self, sx: CSSFloat, sy: CSSFloat) {
*self = ComputedMatrix::new(1.0, sx,
sy, 1.0,
computed::LengthAndPercentage::zero(),
computed::LengthAndPercentage::zero()) *
(*self).clone()
}
pub fn translate(&mut self,
tx: computed::LengthAndPercentage,
ty: computed::LengthAndPercentage) {
*self = ComputedMatrix::new(1.0, 0.0, 0.0, 1.0, tx, ty) * (*self).clone()
}
pub fn rotate(&mut self, theta: CSSFloat) {
*self = ComputedMatrix::new(theta.cos(), -theta.sin(),
theta.sin(), theta.cos(),
computed::LengthAndPercentage::zero(),
computed::LengthAndPercentage::zero()) *
(*self).clone()
}
}
pub type T = Option<ComputedMatrix>;
} }
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct SpecifiedMatrix { pub struct SpecifiedMatrix {
m11: CSSFloat, m12: CSSFloat, m11: CSSFloat, m12: CSSFloat, m13: CSSFloat, m14: CSSFloat,
m21: CSSFloat, m22: CSSFloat, m21: CSSFloat, m22: CSSFloat, m23: CSSFloat, m24: CSSFloat,
m31: specified::LengthAndPercentage, m32: specified::LengthAndPercentage, m31: CSSFloat, m32: CSSFloat, m33: CSSFloat, m34: CSSFloat,
m41: specified::LengthAndPercentage,
m42: specified::LengthAndPercentage,
m43: specified::Length,
m44: CSSFloat,
} }
impl ToCss for SpecifiedMatrix { impl ToCss for SpecifiedMatrix {
@ -3207,38 +3152,6 @@ pub mod longhands {
} }
} }
impl Mul<SpecifiedMatrix> for SpecifiedMatrix {
type Output = SpecifiedMatrix;
fn mul(self, other: SpecifiedMatrix) -> SpecifiedMatrix {
SpecifiedMatrix {
m11: self.m11*other.m11 + self.m12*other.m21,
m12: self.m11*other.m12 + self.m12*other.m22,
m21: self.m21*other.m11 + self.m22*other.m21,
m22: self.m21*other.m12 + self.m22*other.m22,
m31: self.m31.clone()*other.m11 + self.m32.clone()*other.m21 + other.m31,
m32: self.m31*other.m12 + self.m32*other.m22 + other.m32,
}
}
}
impl SpecifiedMatrix {
#[inline]
fn new(m11: CSSFloat,
m12: CSSFloat,
m21: CSSFloat,
m22: CSSFloat,
m31: specified::LengthAndPercentage,
m32: specified::LengthAndPercentage)
-> SpecifiedMatrix {
SpecifiedMatrix {
m11: m11, m12: m12,
m21: m21, m22: m22,
m31: m31, m32: m32,
}
}
}
fn parse_two_lengths_or_percentages(input: &mut Parser) fn parse_two_lengths_or_percentages(input: &mut Parser)
-> Result<(specified::LengthAndPercentage, -> Result<(specified::LengthAndPercentage,
specified::LengthAndPercentage),()> { specified::LengthAndPercentage),()> {
@ -3262,10 +3175,13 @@ pub mod longhands {
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
enum SpecifiedOperation { enum SpecifiedOperation {
Matrix(SpecifiedMatrix), Matrix(SpecifiedMatrix),
Translate(specified::LengthAndPercentage, specified::LengthAndPercentage),
Scale(CSSFloat, CSSFloat),
Rotate(specified::Angle),
Skew(CSSFloat, CSSFloat), Skew(CSSFloat, CSSFloat),
Translate(specified::LengthAndPercentage,
specified::LengthAndPercentage,
specified::Length),
Scale(CSSFloat, CSSFloat, CSSFloat),
Rotate(CSSFloat, CSSFloat, CSSFloat, specified::Angle),
Perspective(specified::Length),
} }
impl ToCss for SpecifiedOperation { impl ToCss for SpecifiedOperation {
@ -3318,23 +3234,54 @@ pub mod longhands {
if values.len() != 6 { if values.len() != 6 {
return Err(()) return Err(())
} }
let (tx, ty) = let (tx, ty, tz) =
(specified::Length::Absolute(Au::from_f32_px(values[4])), (specified::Length::Absolute(Au::from_f32_px(values[4])),
specified::Length::Absolute(Au::from_f32_px(values[5]))); specified::Length::Absolute(Au::from_f32_px(values[5])),
specified::Length::Absolute(Au(0)));
let (tx, ty) = let (tx, ty) =
(specified::LengthAndPercentage::from_length(tx), (specified::LengthAndPercentage::from_length(tx),
specified::LengthAndPercentage::from_length(ty)); specified::LengthAndPercentage::from_length(ty));
result.push(SpecifiedOperation::Matrix( result.push(SpecifiedOperation::Matrix(
SpecifiedMatrix::new(values[0], values[1], SpecifiedMatrix {
values[2], values[3], m11: values[0], m12: values[1], m13: 0.0, m14: 0.0,
tx, ty))); m21: values[2], m22: values[3], m23: 0.0, m24: 0.0,
m31: 0.0, m32: 0.0, m33: 1.0, m34: 0.0,
m41: tx, m42: ty, m43: tz, m44: 1.0
}));
Ok(())
}))
},
"matrix3d" => {
try!(input.parse_nested_block(|input| {
let values = try!(input.parse_comma_separated(|input| {
input.expect_number()
}));
if values.len() != 16 {
return Err(())
}
let (tx, ty, tz) =
(specified::Length::Absolute(Au::from_f32_px(values[12])),
specified::Length::Absolute(Au::from_f32_px(values[13])),
specified::Length::Absolute(Au::from_f32_px(values[14])));
let (tx, ty) =
(specified::LengthAndPercentage::from_length(tx),
specified::LengthAndPercentage::from_length(ty));
result.push(SpecifiedOperation::Matrix(
SpecifiedMatrix {
m11: values[ 0], m12: values[ 1], m13: values[ 2], m14: values[ 3],
m21: values[ 4], m22: values[ 5], m23: values[ 6], m24: values[ 7],
m31: values[ 8], m32: values[ 9], m33: values[10], m34: values[11],
m41: tx, m42: ty, m43: tz, m44: values[15]
}));
Ok(()) Ok(())
})) }))
}, },
"translate" => { "translate" => {
try!(input.parse_nested_block(|input| { try!(input.parse_nested_block(|input| {
let (tx, ty) = try!(parse_two_lengths_or_percentages(input)); let (tx, ty) = try!(parse_two_lengths_or_percentages(input));
result.push(SpecifiedOperation::Translate(tx, ty)); result.push(SpecifiedOperation::Translate(tx,
ty,
specified::Length::Absolute(Au(0))));
Ok(()) Ok(())
})) }))
}, },
@ -3344,7 +3291,8 @@ pub mod longhands {
result.push(SpecifiedOperation::Translate( result.push(SpecifiedOperation::Translate(
specified::LengthAndPercentage::from_length_or_percentage( specified::LengthAndPercentage::from_length_or_percentage(
&tx), &tx),
specified::LengthAndPercentage::zero())); specified::LengthAndPercentage::zero(),
specified::Length::Absolute(Au(0))));
Ok(()) Ok(())
})) }))
}, },
@ -3354,35 +3302,114 @@ pub mod longhands {
result.push(SpecifiedOperation::Translate( result.push(SpecifiedOperation::Translate(
specified::LengthAndPercentage::zero(), specified::LengthAndPercentage::zero(),
specified::LengthAndPercentage::from_length_or_percentage( specified::LengthAndPercentage::from_length_or_percentage(
&ty))); &ty),
specified::Length::Absolute(Au(0))));
Ok(()) Ok(())
})) }))
}, },
"translatez" => {
try!(input.parse_nested_block(|input| {
let tz = try!(specified::Length::parse(input));
result.push(SpecifiedOperation::Translate(
specified::LengthAndPercentage::zero(),
specified::LengthAndPercentage::zero(),
tz));
Ok(())
}))
},
"translate3d" => {
try!(input.parse_nested_block(|input| {
let tx = try!(specified::LengthOrPercentage::parse(input));
try!(input.expect_comma());
let ty = try!(specified::LengthOrPercentage::parse(input));
try!(input.expect_comma());
let tz = try!(specified::Length::parse(input));
result.push(SpecifiedOperation::Translate(
specified::LengthAndPercentage::from_length_or_percentage(&tx),
specified::LengthAndPercentage::from_length_or_percentage(&ty),
tz));
Ok(())
}))
},
"scale" => { "scale" => {
try!(input.parse_nested_block(|input| { try!(input.parse_nested_block(|input| {
let (sx, sy) = try!(parse_two_floats(input)); let (sx, sy) = try!(parse_two_floats(input));
result.push(SpecifiedOperation::Scale(sx, sy)); result.push(SpecifiedOperation::Scale(sx, sy, 1.0));
Ok(()) Ok(())
})) }))
}, },
"scalex" => { "scalex" => {
try!(input.parse_nested_block(|input| { try!(input.parse_nested_block(|input| {
let sx = try!(input.expect_number()); let sx = try!(input.expect_number());
result.push(SpecifiedOperation::Scale(sx, 1.0)); result.push(SpecifiedOperation::Scale(sx, 1.0, 1.0));
Ok(()) Ok(())
})) }))
}, },
"scaley" => { "scaley" => {
try!(input.parse_nested_block(|input| { try!(input.parse_nested_block(|input| {
let sy = try!(input.expect_number()); let sy = try!(input.expect_number());
result.push(SpecifiedOperation::Scale(1.0, sy)); result.push(SpecifiedOperation::Scale(1.0, sy, 1.0));
Ok(())
}))
},
"scalez" => {
try!(input.parse_nested_block(|input| {
let sz = try!(input.expect_number());
result.push(SpecifiedOperation::Scale(1.0, 1.0, sz));
Ok(())
}))
},
"scale3d" => {
try!(input.parse_nested_block(|input| {
let sx = try!(input.expect_number());
try!(input.expect_comma());
let sy = try!(input.expect_number());
try!(input.expect_comma());
let sz = try!(input.expect_number());
result.push(SpecifiedOperation::Scale(sx, sy, sz));
Ok(()) Ok(())
})) }))
}, },
"rotate" => { "rotate" => {
try!(input.parse_nested_block(|input| { try!(input.parse_nested_block(|input| {
let theta = try!(specified::Angle::parse(input)); let theta = try!(specified::Angle::parse(input));
result.push(SpecifiedOperation::Rotate(theta)); result.push(SpecifiedOperation::Rotate(0.0, 0.0, 1.0, theta));
Ok(())
}))
},
"rotatex" => {
try!(input.parse_nested_block(|input| {
let theta = try!(specified::Angle::parse(input));
result.push(SpecifiedOperation::Rotate(1.0, 0.0, 0.0, theta));
Ok(())
}))
},
"rotatey" => {
try!(input.parse_nested_block(|input| {
let theta = try!(specified::Angle::parse(input));
result.push(SpecifiedOperation::Rotate(0.0, 1.0, 0.0, theta));
Ok(())
}))
},
"rotatez" => {
try!(input.parse_nested_block(|input| {
let theta = try!(specified::Angle::parse(input));
result.push(SpecifiedOperation::Rotate(0.0, 0.0, 1.0, theta));
Ok(())
}))
},
"rotate3d" => {
try!(input.parse_nested_block(|input| {
let ax = try!(input.expect_number());
try!(input.expect_comma());
let ay = try!(input.expect_number());
try!(input.expect_comma());
let az = try!(input.expect_number());
try!(input.expect_comma());
let theta = try!(specified::Angle::parse(input));
// TODO(gw): Check the axis can be normalized!!
result.push(SpecifiedOperation::Rotate(ax, ay, az, theta));
Ok(()) Ok(())
})) }))
}, },
@ -3406,6 +3433,13 @@ pub mod longhands {
result.push(SpecifiedOperation::Skew(1.0, sy)); result.push(SpecifiedOperation::Skew(1.0, sy));
Ok(()) Ok(())
})) }))
},
"perspective" => {
try!(input.parse_nested_block(|input| {
let d = try!(specified::Length::parse(input));
result.push(SpecifiedOperation::Perspective(d));
Ok(())
}))
} }
_ => return Err(()) _ => return Err(())
} }
@ -3427,32 +3461,41 @@ pub mod longhands {
return None return None
} }
let mut result = computed_value::ComputedMatrix::identity(); let mut result = vec!();
for operation in self.0.iter() { for operation in self.0.iter() {
match *operation { match *operation {
SpecifiedOperation::Matrix(ref matrix) => { SpecifiedOperation::Matrix(ref matrix) => {
result = computed_value::ComputedMatrix { let m = computed_value::ComputedMatrix {
m11: matrix.m11, m12: matrix.m12, m11: matrix.m11, m12: matrix.m12, m13: matrix.m13, m14: matrix.m14,
m21: matrix.m21, m22: matrix.m22, m21: matrix.m21, m22: matrix.m22, m23: matrix.m23, m24: matrix.m24,
m31: matrix.m31.to_computed_value(context), m31: matrix.m31, m32: matrix.m32, m33: matrix.m33, m34: matrix.m34,
m32: matrix.m32.to_computed_value(context), m41: matrix.m41.to_computed_value(context),
} * result m42: matrix.m42.to_computed_value(context),
m43: matrix.m43.to_computed_value(context).to_f32_px(),
m44: matrix.m44
};
result.push(computed_value::ComputedOperation::Matrix(m));
} }
SpecifiedOperation::Translate(ref tx, ref ty) => { SpecifiedOperation::Translate(ref tx, ref ty, ref tz) => {
result.translate(tx.to_computed_value(context), result.push(computed_value::ComputedOperation::Translate(tx.to_computed_value(context),
ty.to_computed_value(context)) ty.to_computed_value(context),
tz.to_computed_value(context)));
} }
SpecifiedOperation::Scale(sx, sy) => { SpecifiedOperation::Scale(sx, sy, sz) => {
result.scale(sx, sy) result.push(computed_value::ComputedOperation::Scale(sx, sy, sz));
} }
SpecifiedOperation::Rotate(ref theta) => { SpecifiedOperation::Rotate(ax, ay, az, theta) => {
result.rotate(f32::consts::PI_2 - theta.radians()); result.push(computed_value::ComputedOperation::Rotate(ax, ay, az, theta));
} }
SpecifiedOperation::Skew(sx, sy) => { SpecifiedOperation::Skew(sx, sy) => {
result.skew(sx, sy) result.push(computed_value::ComputedOperation::Skew(sx, sy));
} }
} SpecifiedOperation::Perspective(d) => {
result.push(computed_value::ComputedOperation::Perspective(d.to_computed_value(context)));
}
};
} }
Some(result) Some(result)
} }
} }

2
ports/cef/Cargo.lock generated
View file

@ -366,7 +366,7 @@ dependencies = [
[[package]] [[package]]
name = "geom" name = "geom"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/servo/rust-geom#c4bdb1ef8f4915ae636eb752b103f69246b50304" source = "git+https://github.com/servo/rust-geom#270d0246b79fbf86fc2938c4952cae74e4025fcf"
dependencies = [ dependencies = [
"log 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"num 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "num 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)",

2
ports/gonk/Cargo.lock generated
View file

@ -345,7 +345,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "geom" name = "geom"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/servo/rust-geom#c4bdb1ef8f4915ae636eb752b103f69246b50304" source = "git+https://github.com/servo/rust-geom#270d0246b79fbf86fc2938c4952cae74e4025fcf"
dependencies = [ dependencies = [
"log 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"num 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "num 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)",

View file

@ -312,6 +312,7 @@ flaky_cpu == linebreak_simple_a.html linebreak_simple_b.html
== text_transform_lowercase_a.html text_transform_lowercase_ref.html == text_transform_lowercase_a.html text_transform_lowercase_ref.html
== text_transform_none_a.html text_transform_none_ref.html == text_transform_none_a.html text_transform_none_ref.html
== text_transform_uppercase_a.html text_transform_uppercase_ref.html == text_transform_uppercase_a.html text_transform_uppercase_ref.html
== transform_3d.html transform_3d_ref.html
== transform_simple_a.html transform_simple_ref.html == transform_simple_a.html transform_simple_ref.html
== transform_stacking_context_a.html transform_stacking_context_ref.html == transform_stacking_context_a.html transform_stacking_context_ref.html
== upper_id_attr.html upper_id_attr_ref.html == upper_id_attr.html upper_id_attr_ref.html

127
tests/ref/transform_3d.html Normal file
View file

@ -0,0 +1,127 @@
<!DOCTYPE html>
<html>
<head>
<style type="text/css">
#div_t3d_1 {
position: absolute;
background-color: red;
top: 10px;
left: 10px;
width: 40px;
height: 40px;
}
#div_t3d_2 {
position: absolute;
background-color: green;
top: 10px;
left: 10px;
width: 20px;
height: 20px;
transform: translate3d(50%, 50%, 0);
}
#div_t3d_3 {
position: absolute;
background-color: red;
top: 10px;
left: 60px;
width: 40px;
height: 40px;
}
#div_t3d_4 {
position: absolute;
background-color: green;
top: 10px;
left: 10px;
width: 20px;
height: 20px;
transform: translate3d(-5px, 10px, 0);
}
#div_s3d_1 {
position: absolute;
background-color: red;
top: 10px;
left: 110px;
width: 40px;
height: 40px;
}
#div_s3d_2 {
position: absolute;
background-color: green;
top: 10px;
left: 10px;
width: 20px;
height: 20px;
transform: scale3d(0.5, 2, 1);
}
#div_r3d_1 {
position: absolute;
background-color: red;
top: 60px;
left: 10px;
width: 40px;
height: 40px;
}
#div_r3d_2 {
position: absolute;
background-color: green;
top: 10px;
left: 10px;
width: 10px;
height: 20px;
transform: rotateZ(90deg);
}
#div_r3d_3 {
position: absolute;
background-color: red;
top: 60px;
left: 60px;
width: 40px;
height: 40px;
}
#div_r3d_4 {
position: absolute;
background-color: green;
top: 10px;
left: 10px;
width: 10px;
height: 20px;
transform: rotate3d(0, 0, 1, 90deg);
}
</style>
</head>
<body>
<div id="div_t3d_1">
<div id="div_t3d_2">
</div>
</div>
<div id="div_t3d_3">
<div id="div_t3d_4">
</div>
</div>
<div id="div_s3d_1">
<div id="div_s3d_2">
</div>
</div>
<div id="div_r3d_1">
<div id="div_r3d_2">
</div>
</div>
<div id="div_r3d_3">
<div id="div_r3d_4">
</div>
</div>
</body>
</html>

View file

@ -0,0 +1,122 @@
<!DOCTYPE html>
<html>
<head>
<style type="text/css">
#div_t3d_1 {
position: absolute;
background-color: red;
top: 10px;
left: 10px;
width: 40px;
height: 40px;
}
#div_t3d_2 {
position: absolute;
background-color: green;
top: 20px;
left: 20px;
width: 20px;
height: 20px;
}
#div_t3d_3 {
position: absolute;
background-color: red;
top: 10px;
left: 60px;
width: 40px;
height: 40px;
}
#div_t3d_4 {
position: absolute;
background-color: green;
top: 20px;
left: 5px;
width: 20px;
height: 20px;
}
#div_s3d_1 {
position: absolute;
background-color: red;
top: 10px;
left: 110px;
width: 40px;
height: 40px;
}
#div_s3d_2 {
position: absolute;
background-color: green;
top: 0;
left: 15px;
width: 10px;
height: 40px;
}
#div_r3d_1 {
position: absolute;
background-color: red;
top: 60px;
left: 10px;
width: 40px;
height: 40px;
}
#div_r3d_2 {
position: absolute;
background-color: green;
top: 15px;
left: 5px;
width: 20px;
height: 10px;
}
#div_r3d_3 {
position: absolute;
background-color: red;
top: 60px;
left: 60px;
width: 40px;
height: 40px;
}
#div_r3d_4 {
position: absolute;
background-color: green;
top: 15px;
left: 5px;
width: 20px;
height: 10px;
}
</style>
</head>
<body>
<div id="div_t3d_1">
<div id="div_t3d_2">
</div>
</div>
<div id="div_t3d_3">
<div id="div_t3d_4">
</div>
</div>
<div id="div_s3d_1">
<div id="div_s3d_2">
</div>
</div>
<div id="div_r3d_1">
<div id="div_r3d_2">
</div>
</div>
<div id="div_r3d_3">
<div id="div_r3d_4">
</div>
</div>
</body>
</html>