From 792666ec87a1766fbd792eba1d7e3413aedc59d5 Mon Sep 17 00:00:00 2001 From: Bryan Bell Date: Tue, 20 May 2014 02:20:26 -0700 Subject: [PATCH] Add groove and ridge border support. --- src/components/gfx/render_context.rs | 303 +++++++++++++++++++++++- src/components/style/properties.rs.mako | 4 +- src/test/html/test_border.html | 13 +- 3 files changed, 315 insertions(+), 5 deletions(-) diff --git a/src/components/gfx/render_context.rs b/src/components/gfx/render_context.rs index 95b834c3d5d..46278b97ca3 100644 --- a/src/components/gfx/render_context.rs +++ b/src/components/gfx/render_context.rs @@ -43,6 +43,15 @@ 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 @@ -159,8 +168,14 @@ impl<'a> RenderContext<'a> { 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::ridge => { + self.draw_ridge_border_segment(direction, bounds, border, color_select); + } //FIXME(sammykim): Five more styles should be implemented. - //double, groove, ridge, inset, outset + //double, inset, outset } } @@ -178,8 +193,14 @@ impl<'a> RenderContext<'a> { 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::ridge => { + self.draw_ridge_border_segment(Right, bounds, border, color); + } //FIXME(sankha93): Five more styles should be implemented. - //double, groove, ridge, inset, outset + //double, inset, outset } } @@ -283,6 +304,284 @@ impl<'a> RenderContext<'a> { let path = path_builder.finish(); self.draw_target.fill(&path, &ColorPattern(color), &draw_opts); } + + fn get_scaled_bounds(&self, + bounds : &Rect, + border : SideOffsets2D, + shrink_factor : f32) -> (Point2D, Point2D, Point2D, Point2D) { + + let rect = bounds.to_azure_rect(); + let scaled_border = SideOffsets2D::new(shrink_factor * border.top, + shrink_factor * border.right, + shrink_factor * border.bottom, + shrink_factor * border.left); + + 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); + + let scaled_left_top = left_top + Point2D(scaled_border.left, + scaled_border.top); + let scaled_right_top = right_top + Point2D(-scaled_border.right, + scaled_border.top); + let scaled_left_bottom = left_bottom + Point2D(scaled_border.left, + -scaled_border.bottom); + let scaled_right_bottom = right_bottom + Point2D(-scaled_border.right, + -scaled_border.bottom); + + 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); + + // 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 * 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); + } + } + + } + + 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(); + 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)); + } + 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_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)); + } + 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(right_bottom); + } + } + let path = path_builder.finish(); + 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 08d4c3f3968..c510e71924b 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 groove ridge insed outset - ${single_keyword("border-top-style", values="none solid dotted dashed hidden")} + // double insed outset + ${single_keyword("border-top-style", values="none solid dotted dashed hidden groove ridge")} % 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 7015102d726..1446598ad59 100755 --- a/src/test/html/test_border.html +++ b/src/test/html/test_border.html @@ -26,6 +26,16 @@ border-width: 10px; border-color: green red yellow black; } +#groove{ + border-style: groove; + border-width: 10px; + border-color: green red yellow black; +} +#ridge{ + border-style: ridge; + border-width: 10px; + border-color: green red yellow black; +} #diamond1{ width: 0; height: 0; @@ -47,8 +57,9 @@
hidden test.
solid test
dashed test
-
dotted test
+
groove test
+
ridge test