From 2bb5c8bdf3f4a2a4a483760cdc198696cc54aabc Mon Sep 17 00:00:00 2001 From: Bryan Bell Date: Tue, 20 May 2014 23:22:28 -0700 Subject: [PATCH] Refactor groove & ridge border support, add inset & outset border support Significant cleanup of border support, adds inset & outset border code. Border rendering matches Chrome's border rendering. --- src/components/gfx/render_context.rs | 386 ++++++------------------ src/components/style/properties.rs.mako | 4 +- src/test/html/test_border.html | 13 + src/test/ref/borders.png | Bin 3075 -> 2993 bytes src/test/ref/borders_a.html | 27 +- 5 files changed, 137 insertions(+), 293 deletions(-) diff --git a/src/components/gfx/render_context.rs b/src/components/gfx/render_context.rs index e58c318f5c5..b632a628651 100644 --- a/src/components/gfx/render_context.rs +++ b/src/components/gfx/render_context.rs @@ -43,15 +43,6 @@ enum DashSize { DashedBorder = 3 } -enum ShortEnd { - TopShort, - LeftShort, - RightShort, - BottomShort, - NoneShort, - AllShort, -} - impl<'a> RenderContext<'a> { pub fn get_draw_target(&self) -> &'a DrawTarget { self.draw_target @@ -154,28 +145,26 @@ impl<'a> RenderContext<'a> { }; match style_select{ - border_style::none => { + border_style::none => { } - border_style::hidden => { + border_style::hidden => { } //FIXME(sammykim): This doesn't work with dash_pattern and cap_style well. I referred firefox code. - border_style::dotted => { + border_style::dotted => { self.draw_dashed_border_segment(direction, bounds, border, color_select, DottedBorder); } - border_style::dashed => { + border_style::dashed => { self.draw_dashed_border_segment(direction, bounds, border, color_select, DashedBorder); } - border_style::solid => { + border_style::solid => { self.draw_solid_border_segment(direction,bounds,border,color_select); } - border_style::groove => { - self.draw_groove_border_segment(direction, bounds, border, color_select); + border_style::groove | border_style::ridge => { + self.draw_groove_ridge_border_segment(direction, bounds, border, color_select, style_select); } - border_style::ridge => { - self.draw_ridge_border_segment(direction, bounds, border, color_select); + border_style::inset | border_style::outset => { + self.draw_inset_outset_border_segment(direction, bounds, border, style_select, color_select); } - //FIXME(sammykim): Five more styles should be implemented. - //double, inset, outset } } @@ -183,32 +172,30 @@ impl<'a> RenderContext<'a> { let border = SideOffsets2D::new_all_same(bounds.size.width).to_float_px(); match style{ - border_style::none | border_style::hidden => {} - border_style::dotted => { + border_style::none | border_style::hidden => {} + border_style::dotted => { self.draw_dashed_border_segment(Right, bounds, border, color, DottedBorder); } - border_style::dashed => { + border_style::dashed => { self.draw_dashed_border_segment(Right, bounds, border, color, DashedBorder); } - border_style::solid => { + border_style::solid => { self.draw_solid_border_segment(Right,bounds,border,color); } - border_style::groove => { - self.draw_groove_border_segment(Right, bounds, border, color); + border_style::groove | border_style::ridge => { + self.draw_groove_ridge_border_segment(Right, bounds, border, color, style); } - border_style::ridge => { - self.draw_ridge_border_segment(Right, bounds, border, color); + border_style::inset | border_style::outset => { + self.draw_inset_outset_border_segment(Right, bounds, border, style, color); } - //FIXME(sankha93): Five more styles should be implemented. - //double, inset, outset } } - fn draw_dashed_border_segment(&self, - direction: Direction, - bounds: &Rect, - border: SideOffsets2D, - color: Color, + fn draw_dashed_border_segment(&self, + direction: Direction, + bounds: &Rect, + border: SideOffsets2D, + color: Color, dash_size: DashSize) { let rect = bounds.to_azure_rect(); let draw_opts = DrawOptions(1 as AzFloat, 0 as uint16_t); @@ -266,43 +253,11 @@ impl<'a> RenderContext<'a> { fn draw_solid_border_segment(&self, direction: Direction, bounds: &Rect, border: SideOffsets2D, color: Color) { let rect = bounds.to_azure_rect(); - let draw_opts = DrawOptions(1.0 , 0); - let path_builder = self.draw_target.create_path_builder(); - let left_top = Point2D(rect.origin.x, rect.origin.y); let right_top = Point2D(rect.origin.x + rect.size.width, rect.origin.y); let left_bottom = Point2D(rect.origin.x, rect.origin.y + rect.size.height); let right_bottom = Point2D(rect.origin.x + rect.size.width, rect.origin.y + rect.size.height); - - match direction { - Top => { - path_builder.move_to(left_top); - path_builder.line_to(right_top); - path_builder.line_to(right_top + Point2D(-border.right, border.top)); - path_builder.line_to(left_top + Point2D(border.left, border.top)); - } - Left => { - path_builder.move_to(left_top); - path_builder.line_to(left_top + Point2D(border.left, border.top)); - path_builder.line_to(left_bottom + Point2D(border.left, -border.bottom)); - path_builder.line_to(left_bottom); - } - Right => { - path_builder.move_to(right_top); - path_builder.line_to(right_bottom); - path_builder.line_to(right_bottom + Point2D(-border.right, -border.bottom)); - path_builder.line_to(right_top + Point2D(-border.right, border.top)); - } - Bottom => { - path_builder.move_to(left_bottom); - path_builder.line_to(left_bottom + Point2D(border.left, -border.bottom)); - path_builder.line_to(right_bottom + Point2D(-border.right, -border.bottom)); - path_builder.line_to(right_bottom); - } - } - - let path = path_builder.finish(); - self.draw_target.fill(&path, &ColorPattern(color), &draw_opts); + self.draw_border_path((left_top, right_top, left_bottom, right_bottom), direction, border, color); } fn get_scaled_bounds(&self, @@ -333,200 +288,99 @@ impl<'a> RenderContext<'a> { return (scaled_left_top, scaled_right_top, scaled_left_bottom, scaled_right_bottom); } - fn draw_groove_border_segment(&self, - direction: Direction, - bounds: &Rect, - border: SideOffsets2D, - color: Color) { - let rect = bounds.to_azure_rect(); - let left_top = Point2D(rect.origin.x, rect.origin.y); - let right_top = Point2D(rect.origin.x + rect.size.width, rect.origin.y); - let left_bottom = Point2D(rect.origin.x, rect.origin.y + rect.size.height); - let right_bottom = Point2D(rect.origin.x + rect.size.width, rect.origin.y + rect.size.height); + fn draw_groove_ridge_border_segment(&self, + direction: Direction, + bounds: &Rect, + border: SideOffsets2D, + color: Color, + style: border_style::T) { + // original bounds as a 4 element tuple, with no scaling. + let original_bounds = self.get_scaled_bounds(bounds, border, 0.0); + // shrink the bounds by 1/2 of the border, leaving the innermost 1/2 of the border + let inner_scaled_bounds = self.get_scaled_bounds(bounds, border, 0.5); + let scaled_border = SideOffsets2D::new(0.5 * border.top, + 0.5 * border.right, + 0.5 * border.bottom, + 0.5 * border.left); + let is_groove = match style { + border_style::groove => true, + border_style::ridge => false, + _ => { assert!(false, "invalid border style"); false } + }; - // shrink the bounds by 2/3 of the border, leaving the innermost 1/3 of the border - let (inner_left_top, inner_right_top, inner_left_bottom, inner_right_bottom) = - self.get_scaled_bounds(bounds, border, 2.0/3.0); + let darker_color = self.scale_color(color, if is_groove { 1.0/3.0 } else { 2.0/3.0 }); - // shrink the bounds by 1/3 of the border, leaving the innermost 2/3 of the border - let (middle_left_top, middle_right_top, middle_left_bottom, middle_right_bottom) = - self.get_scaled_bounds(bounds, border, 1.0/3.0); - - let scaled_border = SideOffsets2D::new((1.0/3.0) * border.top, - (1.0/3.0) * border.right, - (1.0/3.0) * border.bottom, - (1.0/3.0) * border.left); - let darker_color = Color(color.r * 1.0/3.0, color.g * 1.0/3.0, color.b * 1.0/3.0, color.a); - match direction { - Top => { - // outer portion of the border - self.draw_short_ended_path(left_top, right_top, left_bottom, right_bottom, - direction, scaled_border, darker_color, LeftShort); - // middle portion of the border - self.draw_short_ended_path(middle_left_top, middle_right_top, middle_left_bottom, - middle_right_bottom, direction, scaled_border, darker_color, - LeftShort); - // inner portion of the border - self.draw_short_ended_path(inner_left_top, inner_right_top, inner_left_bottom, - inner_right_bottom, direction, scaled_border, color, - LeftShort); - } - Left => { - // outer portion of the border - self.draw_short_ended_path(left_top, right_top, left_bottom, right_bottom, - direction, scaled_border, darker_color, NoneShort); - // middle portion of the border - self.draw_short_ended_path(middle_left_top, middle_right_top, middle_left_bottom, - middle_right_bottom, direction, scaled_border, darker_color, - NoneShort); - // inner portion of the border - self.draw_short_ended_path(inner_left_top, inner_right_top, inner_left_bottom, - inner_right_bottom, direction, scaled_border, color, - NoneShort); - } - Right => { - // outer portion of the border - self.draw_short_ended_path(left_top, right_top, left_bottom, right_bottom, - direction, scaled_border, color, AllShort); - // middle portion of the border - self.draw_short_ended_path(middle_left_top, middle_right_top, middle_left_bottom, - middle_right_bottom, direction, scaled_border, darker_color, - AllShort); - // inner portion of the border - self.draw_short_ended_path(inner_left_top, inner_right_top, inner_left_bottom, - inner_right_bottom, direction, scaled_border, darker_color, - AllShort); - } - Bottom => { - // outer portion of the border - self.draw_short_ended_path(left_top, right_top, left_bottom, right_bottom, - direction, scaled_border, color, LeftShort); - // middle portion of the border - self.draw_short_ended_path(middle_left_top, middle_right_top, middle_left_bottom, - middle_right_bottom, direction, scaled_border, darker_color, - LeftShort); - // inner portion of the border - self.draw_short_ended_path(inner_left_top, inner_right_top, inner_left_bottom, - inner_right_bottom, direction, scaled_border, darker_color, - LeftShort); - } - } - } - - fn draw_ridge_border_segment(&self, - direction: Direction, - bounds: &Rect, - border: SideOffsets2D, - color: Color) { - let rect = bounds.to_azure_rect(); - let left_top = Point2D(rect.origin.x, rect.origin.y); - let right_top = Point2D(rect.origin.x + rect.size.width, rect.origin.y); - let left_bottom = Point2D(rect.origin.x, rect.origin.y + rect.size.height); - let right_bottom = Point2D(rect.origin.x + rect.size.width, rect.origin.y + rect.size.height); - - // shrink the bounds by 2/3 of the border, leaving the innermost 1/3 of the border - let (inner_left_top, inner_right_top, inner_left_bottom, inner_right_bottom) = - self.get_scaled_bounds(bounds, border, 2.0/3.0); - - // shrink the bounds by 1/3 of the border, leaving the innermost 2/3 of the border - let (middle_left_top, middle_right_top, middle_left_bottom, middle_right_bottom) = - self.get_scaled_bounds(bounds, border, 1.0/3.0); - - let scaled_border = SideOffsets2D::new((1.0/3.0) * border.top, - (1.0/3.0) * border.right, - (1.0/3.0) * border.bottom, - (1.0/3.0) * border.left); - let darker_color = Color(color.r * 2.0/3.0, color.g * 2.0/3.0, color.b * 2.0/3.0, color.a); - match direction { - Top => { - // outer portion of the border - self.draw_short_ended_path(left_top, right_top, left_bottom, right_bottom, - direction, scaled_border, color, LeftShort); - // middle portion of the border - self.draw_short_ended_path(middle_left_top, middle_right_top, middle_left_bottom, - middle_right_bottom, direction, scaled_border, color, - LeftShort); - // inner portion of the border - self.draw_short_ended_path(inner_left_top, inner_right_top, inner_left_bottom, - inner_right_bottom, direction, scaled_border, darker_color, - LeftShort); - } - Left => { - // outer portion of the border - self.draw_short_ended_path(left_top, right_top, left_bottom, right_bottom, - direction, scaled_border, color, NoneShort); - // middle portion of the border - self.draw_short_ended_path(middle_left_top, middle_right_top, middle_left_bottom, - middle_right_bottom, direction, scaled_border, color, - NoneShort); - // inner portion of the border - self.draw_short_ended_path(inner_left_top, inner_right_top, inner_left_bottom, - inner_right_bottom, direction, scaled_border, darker_color, - NoneShort); - } - Right => { - // outer portion of the border - self.draw_short_ended_path(left_top, right_top, left_bottom, right_bottom, - direction, scaled_border, darker_color, AllShort); - // middle portion of the border - self.draw_short_ended_path(middle_left_top, middle_right_top, middle_left_bottom, - middle_right_bottom, direction, scaled_border, color, - AllShort); - // inner portion of the border - self.draw_short_ended_path(inner_left_top, inner_right_top, inner_left_bottom, - inner_right_bottom, direction, scaled_border, color, - AllShort); - } - Bottom => { - // outer portion of the border - self.draw_short_ended_path(left_top, right_top, left_bottom, right_bottom, - direction, scaled_border, darker_color, LeftShort); - // middle portion of the border - self.draw_short_ended_path(middle_left_top, middle_right_top, middle_left_bottom, - middle_right_bottom, direction, scaled_border, color, - LeftShort); - // inner portion of the border - self.draw_short_ended_path(inner_left_top, inner_right_top, inner_left_bottom, - inner_right_bottom, direction, scaled_border, color, - LeftShort); - } - } + let (outer_color, inner_color) = match direction { + Top => if is_groove { (darker_color, color) } else { (color, darker_color) }, + Left => if is_groove { (darker_color, color) } else { (color, darker_color) }, + Right => if is_groove { (color, darker_color) } else { (darker_color, color) }, + Bottom => if is_groove { (color, darker_color) } else { (darker_color, color) } + }; + // outer portion of the border + self.draw_border_path(original_bounds, direction, scaled_border, outer_color); + // inner portion of the border + self.draw_border_path(inner_scaled_bounds, direction, scaled_border, inner_color); } - fn draw_square_border_path(&self, - left_top : Point2D, - right_top : Point2D, - left_bottom : Point2D, - right_bottom : Point2D, - direction : Direction, - border : SideOffsets2D, - color : Color) { - let draw_opts = DrawOptions(1.0 , 0); - let path_builder = self.draw_target.create_path_builder(); + fn scale_color(&self, color: Color, scale_factor: f32) -> Color { + return Color(color.r * scale_factor, color.g * scale_factor, color.b * scale_factor, color.a); + } + + fn draw_inset_outset_border_segment(&self, + direction: Direction, + bounds: &Rect, + border: SideOffsets2D, + style: border_style::T, + color: Color) { + let is_inset = match style { + border_style::inset => true, + border_style::outset => false, + _ => { assert!(false, "invalid border style"); false } + }; + // original bounds as a 4 element tuple, with no scaling. + let original_bounds = self.get_scaled_bounds(bounds, border, 0.0); + // select and scale the color appropriately. + let scaled_color = match direction { + Top => self.scale_color(color, if is_inset { 2.0/3.0 } else { 1.0 }), + Left => self.scale_color(color, if is_inset { 1.0/6.0 } else { 0.5 }), + Right => self.scale_color(color, if is_inset { 1.0 } else { 2.0/3.0 }), + Bottom => self.scale_color(color, if is_inset { 1.0 } else { 2.0/3.0 }) + }; + self.draw_border_path(original_bounds, direction, border, scaled_color); + } + + fn draw_border_path(&self, + bounds: (Point2D, Point2D, Point2D, Point2D), + direction: Direction, + border: SideOffsets2D, + color: Color) { + let (left_top, right_top, left_bottom, right_bottom) = bounds; + let draw_opts = DrawOptions(1.0 , 0); + let path_builder = self.draw_target.create_path_builder(); match direction { Top => { path_builder.move_to(left_top); path_builder.line_to(right_top); - path_builder.line_to(right_top + Point2D(0.0, border.top)); - path_builder.line_to(left_top + Point2D(0.0, border.top)); + path_builder.line_to(right_top + Point2D(-border.right, border.top)); + path_builder.line_to(left_top + Point2D(border.left, border.top)); } Left => { path_builder.move_to(left_top); - path_builder.line_to(left_top + Point2D(border.left, 0.0)); - path_builder.line_to(left_bottom + Point2D(border.left, 0.0)); + path_builder.line_to(left_top + Point2D(border.left, border.top)); + path_builder.line_to(left_bottom + Point2D(border.left, -border.bottom)); path_builder.line_to(left_bottom); } Right => { path_builder.move_to(right_top); path_builder.line_to(right_bottom); - path_builder.line_to(right_bottom + Point2D(-border.right, 0.0)); - path_builder.line_to(right_top + Point2D(-border.right, 0.0)); + path_builder.line_to(right_bottom + Point2D(-border.right, -border.bottom)); + path_builder.line_to(right_top + Point2D(-border.right, border.top)); } Bottom => { path_builder.move_to(left_bottom); - path_builder.line_to(left_bottom + Point2D(0.0, -border.bottom)); - path_builder.line_to(right_bottom + Point2D(0.0, -border.bottom)); + path_builder.line_to(left_bottom + Point2D(border.left, -border.bottom)); + path_builder.line_to(right_bottom + Point2D(-border.right, -border.bottom)); path_builder.line_to(right_bottom); } } @@ -534,54 +388,6 @@ impl<'a> RenderContext<'a> { self.draw_target.fill(&path, &ColorPattern(color), &draw_opts); } - - fn draw_short_ended_path(&self, - left_top: Point2D, - right_top: Point2D, - left_bottom: Point2D, - right_bottom: Point2D, - direction: Direction, - border: SideOffsets2D, - color: Color, - short_end: ShortEnd) { - - match direction { - Top | Bottom => { - let left_border_short = match short_end { LeftShort | AllShort => border.left, - _ => 0.0 }; - let right_border_short = match short_end { RightShort | AllShort => border.right, - _ => 0.0 }; - self.draw_square_border_path(left_top + Point2D(left_border_short, 0.0), - right_top + Point2D(-right_border_short, 0.0), - left_bottom + Point2D(left_border_short, 0.0), - right_bottom + Point2D(-right_border_short, 0.0), - direction, - SideOffsets2D::new(border.top, - border.right - right_border_short, - border.bottom, - border.left - left_border_short), - color); - } - Left | Right => { - let top_border_short = match short_end { TopShort | AllShort => border.top, - _ => 0.0 }; - let bottom_border_short = match short_end { BottomShort | AllShort => border.bottom, - _ => 0.0 }; - - self.draw_square_border_path(left_top + Point2D(0.0, top_border_short), - right_top + Point2D(0.0, top_border_short), - left_bottom + Point2D(0.0, -bottom_border_short), - right_bottom + Point2D(0.0, -bottom_border_short), - direction, - SideOffsets2D::new(border.top - top_border_short, - border.right, - border.bottom - bottom_border_short, - border.left), - color); - - } - } - } } trait ToAzureRect { diff --git a/src/components/style/properties.rs.mako b/src/components/style/properties.rs.mako index c510e71924b..184a0f58f0e 100644 --- a/src/components/style/properties.rs.mako +++ b/src/components/style/properties.rs.mako @@ -221,8 +221,8 @@ pub mod longhands { ${predefined_type("border-%s-color" % side, "CSSColor", "CurrentColor")} % endfor - // double insed outset - ${single_keyword("border-top-style", values="none solid dotted dashed hidden groove ridge")} + // double + ${single_keyword("border-top-style", values="none solid dotted dashed hidden groove ridge inset outset")} % for side in ["right", "bottom", "left"]: <%self:longhand name="border-${side}-style", no_super="True"> diff --git a/src/test/html/test_border.html b/src/test/html/test_border.html index 1446598ad59..3b81f02982d 100755 --- a/src/test/html/test_border.html +++ b/src/test/html/test_border.html @@ -36,6 +36,17 @@ border-width: 10px; border-color: green red yellow black; } +#inset{ + border-style: inset; + border-width: 10px; + border-color: green red yellow black; +} +#outset{ + border-style: outset; + border-width: 10px; + border-color: green red yellow black; +} + #diamond1{ width: 0; height: 0; @@ -60,6 +71,8 @@
dotted test
groove test
ridge test
+
inset test
+
outset test
diff --git a/src/test/ref/borders.png b/src/test/ref/borders.png index a0abba83ff6bbc71ea86fc0f4ef841d424a2fc52..7de3f724b7c18f0bdd4bd05bdd21077e7196e122 100644 GIT binary patch literal 2993 zcmeAS@N?(olHy`uVBq!ia0y~yU{+vYV2a>i0*Z)=h^jL%a94P`IEGZrd3*PuuZ*LB z!^O<(MT+us_;Re&Na_s+S{MoO|z_4%W{{8B&nphbGgd-Rj3>&G)xP9JEqJRCblRu62znFbI zx#ptz?Vl&kxtG~(`aE%N|C^eW=ZoKO`0Q?0a*&CkgHwm0Va5Ucj9yFsdzP}(-%p$8 z`}g(v`2VNW=lqMEpY~kYTK*=I$ziu{vT`#hsBK_iNMgdzDEl?(cSX$JN~`#F_o7U* zudRvPoOX6rY|1z1|F@43ZOFe#zn|`2TJIOTuV7c~-o;r^V@!A7Tf>bt^s&T8j(_=j z28In{;uzuZ_V)SR;S3BB-I!*5d;T4l)Ui3Z%ta4nA1*K7Iwt~CRL4$*ERCf$vy!JjWj!)G*n2o~O>iDWd9j3$!NTtJIl@J*Vx XT5$DBrI)~#3j>3vtDnm{r-UW|gJNOy literal 3075 zcmeAS@N?(olHy`uVBq!ia0y~yU{+vYV2a>i0*Z)=h^jL%aG&yYaSW-L^Y-q+z}o>L z4uMk^&ib;r%HXYo?Go8k=Q;CxRNV{SHdv-VWxV$zwfy<%pYx`r?qp_I@cC`MJ_AER zHj5a8gHHk@!x2ozyX$p^wtnJOr=LCyGS9zRneu$%T=z0LC?mf5`CDH9{oTSk3=K05 zFfnv+;%D60qNDwNj^FI>)7JUEt^NJByt4gj{q^1G#%|xTnP?MV`M$gS_}bj%rw?C? zU7dDzmTC62HIbXmjeakCzpb!|l|eu_f`P%X5kDh(|H<0V_b%BlH_Nx!dwZArRiF`I zv%Y;xn~ObQFoWvfL$&Ykuis{4cpw}ZcjpNoLxCt0H-o~q&!^|v>aj5F+w=7J$Eofu z3=H9l8yFapKyqdK-=FXIm*M{?$G{+ev*yOf<9;xyZJz}n>VNj$&Bk!xZ2`<58*BSH zp8s^pW?-nvNoKX3|1(-0*>@X1&p#OTb6N%igI_UR%jbTM<;S(xFfx4jsl&g#{v8X@ zUNw*{mp6Ud&$2wz>f`0ZuG1MAez45rTd)6YYF`mdD>&3{|CC&_xbpRM1_txA%71@< z{kW74)V$6G6e{QLKdHO)>B>5w*&B1~DqiU$T>oD1;VJc}j0|~qBOIU0iCY~+P8@Uh zpZt5WmxqC2`I*hL?=Ui~a{`$bCNIdu@WBjj2H26HP#R^71_C6%jHZOqlrWkSMsvYv hE*Q-P!!Z}wGcKEM>Z{YcrV7}&XYh3Oa{)GungEoejVJ&B diff --git a/src/test/ref/borders_a.html b/src/test/ref/borders_a.html index 4bd08b11a2a..ee7bd78db7b 100644 --- a/src/test/ref/borders_a.html +++ b/src/test/ref/borders_a.html @@ -15,7 +15,7 @@ #solid{ border-style: solid; border-width: 10px; - border-color: yellow green red black; + border-color: yellow; } #dashed{ border-style: dashed; @@ -31,20 +31,45 @@ border-style: groove; border-width: 10px; border-color: green red yellow black; + position: relative; + left: -30px; + width:1024px; } #ridge{ border-style: ridge; border-width: 10px; border-color: green red yellow black; + position: relative; + left: -30px; + width:1024px; +} +#inset{ + border-style: inset; + border-width: 10px; + border-color: green red yellow black; + position: relative; + left: -30px; + width:1024px; +} +#outset{ + border-style: outset; + border-width: 10px; + border-color: green red yellow black; + position: relative; + left: -30px; + width:1024px; }
+
+
+