changeset 5628:ed14640dd11c

RT-28692: implement curve radii syntax from CSS background and borders module level 3 for -fx-background-radius and -fx-border-radius. Reviewed by: Jonathan
author David Grieve<david.grieve@oracle.com>
date Mon, 04 Nov 2013 20:57:08 -0500
parents b937270b6da0
children 92bc2b511939
files modules/graphics/src/main/docs/javafx/scene/doc-files/cssref.html modules/graphics/src/main/java/com/sun/javafx/css/Size.java modules/graphics/src/main/java/com/sun/javafx/css/StyleConverterImpl.java modules/graphics/src/main/java/com/sun/javafx/css/parser/CSSParser.java modules/graphics/src/main/java/javafx/scene/layout/Background.java modules/graphics/src/main/java/javafx/scene/layout/BackgroundConverter.java modules/graphics/src/main/java/javafx/scene/layout/Border.java modules/graphics/src/main/java/javafx/scene/layout/BorderConverter.java modules/graphics/src/main/java/javafx/scene/layout/CornerRadiiConverter.java modules/graphics/src/test/java/javafx/scene/layout/RegionCSSTest.java
diffstat 10 files changed, 312 insertions(+), 95 deletions(-) [+]
line wrap: on
line diff
--- a/modules/graphics/src/main/docs/javafx/scene/doc-files/cssref.html	Tue Nov 05 07:57:05 2013 +1300
+++ b/modules/graphics/src/main/docs/javafx/scene/doc-files/cssref.html	Mon Nov 04 20:57:08 2013 -0500
@@ -2277,25 +2277,13 @@
         </tr>
         <tr>
           <td class="propertyname">-fx-background-radius</td>
-          <td class="value"><a href="#typesize" class="typelink">&lt;size&gt;</a>
-            | <a href="#typesize" class="typelink">&lt;size&gt;</a> <a href="#typesize"
-              class="typelink">&lt;size&gt;</a>
-            <a href="#typesize" class="typelink">&lt;size&gt;</a> <a href="#typesize"
-              class="typelink">&lt;size&gt;</a>
-            [ , [ <a href="#typesize" class="typelink">&lt;size&gt;</a> | <a href="#typesize"
-              class="typelink">&lt;size&gt;</a>
-            <a href="#typesize" class="typelink">&lt;size&gt;</a> <a href="#typesize"
-              class="typelink">&lt;size&gt;</a>
-            <a href="#typesize" class="typelink">&lt;size&gt;</a>] ]*</td>
-          <td class="default">0 0 0 0</td>
-          <td>
-            <p>A series of radius values or sets of four radius values,
-              separated by commas. A single radius value means the radius of all
-              four corners is the same. Otherwise, the four values in the set
-              determine the radii of the top-left, top-right, bottom-right, and
-              bottom-left corners, in that order. Each comma-separated value or
-              set of values in the series applies to the corresponding
-              background color.</p>
+            <td class="value">[<a href="#typesize" class="typelink">&lt;size&gt;</a>]{1,4} [ / [<a href="#typesize" class="typelink">&lt;size&gt;</a>]{1,4} ]?
+                [ , [<a href="#typesize" class="typelink">&lt;size&gt;</a>]{1,4} [ / [<a href="#typesize" class="typelink">&lt;size&gt;</a>]{1,4} ]? ]*
+            <td class="default">0 0 0 0</td>
+            <td>
+                <p>The same syntax and semenatics as CSS Backgrounds and Borders Module Level 3: <a href="http://www.w3.org/TR/css3-background/#the-border-radius">Curve Radii</a>
+                applies to -fx-background-radius. Note that JavaFX supports only the short-hand syntax.</p>
+                <p>Each comma-separated value or set of values in the series applies to the corresponding background color.</p>
           </td>
         </tr>
         <tr>
@@ -2423,25 +2411,13 @@
         </tr>
         <tr>
           <td class="propertyname">-fx-border-radius</td>
-          <td class="value"><a href="#typesize" class="typelink">&lt;size&gt;</a>
-            | <a href="#typesize" class="typelink">&lt;size&gt;</a> <a href="#typesize"
-              class="typelink">&lt;size&gt;</a>
-            <a href="#typesize" class="typelink">&lt;size&gt;</a> <a href="#typesize"
-              class="typelink">&lt;size&gt;</a>
-            [ , [ <a href="#typesize" class="typelink">&lt;size&gt;</a> | <a href="#typesize"
-              class="typelink">&lt;size&gt;</a>
-            <a href="#typesize" class="typelink">&lt;size&gt;</a> <a href="#typesize"
-              class="typelink">&lt;size&gt;</a>
-            <a href="#typesize" class="typelink">&lt;size&gt;</a>] ]*</td>
+          <td class="value">[<a href="#typesize" class="typelink">&lt;size&gt;</a>]{1,4} [ / [<a href="#typesize" class="typelink">&lt;size&gt;</a>]{1,4} ]?
+            [ , [<a href="#typesize" class="typelink">&lt;size&gt;</a>]{1,4} [ / [<a href="#typesize" class="typelink">&lt;size&gt;</a>]{1,4} ]? ]*
           <td class="default">null</td>
           <td>
-            <p>A series of radius or sets of four radius values, separated by
-              commas. For each item in the series, a single radius value means
-              that all corner radii are the same; and if a set of four radius
-              values is specified, they are used as the radii of the top-left,
-              top-right, bottom-right, and bottom-left corners, in that order.
-              Each item in the series of radii applies to the corresponding item
-              in the series of border colors.</p>
+            <p>Refer to CSS Backgrounds and Borders Module Level 3: <a href="http://www.w3.org/TR/css3-background/#the-border-radius">Curve Radii</a>.
+            JavaFX supports only the short-hand syntax.</p>
+            <p>Each comma-separated value or set of values in the series applies to the corresponding border color.</p>
           </td>
         </tr>
         <tr>
--- a/modules/graphics/src/main/java/com/sun/javafx/css/Size.java	Tue Nov 05 07:57:05 2013 +1300
+++ b/modules/graphics/src/main/java/com/sun/javafx/css/Size.java	Mon Nov 04 20:57:08 2013 -0500
@@ -117,28 +117,38 @@
 
     @Override public boolean equals(Object obj) {
         if (this == obj) return true;
-        if (obj instanceof Size) {
-            final Size other = (Size)obj;
-            if (units == other.units &&
-                (value > 0) ? other.value > 0 : other.value < 0) {
-                //
-                // double == double is not reliable since a double is kind of
-                // a fuzzy value. And Double.compare is too precise.
-                // For javafx, most sizes are rounded to the nearest tenth
-                // (see SizeUnits.round) so comparing  here to the nearest
-                // millionth is more than adequate. In the case of rads and
-                // percents, this is also be more than adequate.
-                //
-                // Math.abs is too slow!
-                final double v0 = value > 0 ? value : -value;
-                final double v1 = other.value > 0 ? other.value : -other.value;
-                final double diff = value  - other.value;
-                if (diff < -0.000001 || 0.000001 < diff) {
-                    return false;
-                }
 
-                return true;
+        if (obj == null || obj.getClass() != this.getClass()) {
+            return false;
+        }
+        final Size other = (Size)obj;
+
+        if (units != other.units) {
+            return false;
+        }
+
+        if (value == other.value) {
+            return true;
+        }
+
+        if ((value > 0) ? other.value > 0 : other.value < 0) {
+            //
+            // double == double is not reliable since a double is kind of
+            // a fuzzy value. And Double.compare is too precise.
+            // For javafx, most sizes are rounded to the nearest tenth
+            // (see SizeUnits.round) so comparing  here to the nearest
+            // millionth is more than adequate. In the case of rads and
+            // percents, this is also be more than adequate.
+            //
+            // Math.abs is too slow!
+            final double v0 = value > 0 ? value : -value;
+            final double v1 = other.value > 0 ? other.value : -other.value;
+            final double diff = value  - other.value;
+            if (diff < -0.000001 || 0.000001 < diff) {
+                return false;
             }
+
+            return true;
         }
         return false;
     }
--- a/modules/graphics/src/main/java/com/sun/javafx/css/StyleConverterImpl.java	Tue Nov 05 07:57:05 2013 +1300
+++ b/modules/graphics/src/main/java/com/sun/javafx/css/StyleConverterImpl.java	Mon Nov 04 20:57:08 2013 -0500
@@ -265,6 +265,9 @@
         case "com.sun.javafx.scene.layout.region.Margins$SequenceConverter" :
             styleConverter = com.sun.javafx.scene.layout.region.Margins.SequenceConverter.getInstance();
             break;
+        case "javafx.scene.layout.CornerRadiiConverter" :
+            styleConverter = javafx.scene.layout.CornerRadiiConverter.getInstance();
+            break;
 
         // parser stuff
         case "com.sun.javafx.css.parser.DeriveColorConverter" :
--- a/modules/graphics/src/main/java/com/sun/javafx/css/parser/CSSParser.java	Tue Nov 05 07:57:05 2013 +1300
+++ b/modules/graphics/src/main/java/com/sun/javafx/css/parser/CSSParser.java	Mon Nov 04 20:57:08 2013 -0500
@@ -34,6 +34,8 @@
 import javafx.scene.layout.BackgroundSize;
 import javafx.scene.layout.BorderStrokeStyle;
 import javafx.scene.layout.BorderWidths;
+import javafx.scene.layout.CornerRadii;
+import javafx.scene.layout.CornerRadiiConverter;
 import javafx.scene.paint.Color;
 import javafx.scene.paint.CycleMethod;
 import javafx.scene.paint.Paint;
@@ -58,7 +60,6 @@
 import com.sun.javafx.css.Size;
 import com.sun.javafx.css.SizeUnits;
 import com.sun.javafx.css.StyleManager;
-import javafx.css.Styleable;
 import com.sun.javafx.css.Stylesheet;
 import com.sun.javafx.css.converters.BooleanConverter;
 import com.sun.javafx.css.converters.EffectConverter;
@@ -719,7 +720,7 @@
         } else if ("-fx-background-position".equals(prop)) {
              return parseBackgroundPositionLayers(root);
         } else if ("-fx-background-radius".equals(prop)) {
-             return parseInsetsLayers(root);
+            return parseCornerRadius(root);
         } else if ("-fx-background-repeat".equals(prop)) {
              return parseBackgroundRepeatStyleLayers(root);
         } else if ("-fx-background-size".equals(prop)) {
@@ -729,7 +730,7 @@
         } else if ("-fx-border-insets".equals(prop)) {
              return parseInsetsLayers(root);
         } else if ("-fx-border-radius".equals(prop)) {
-             return parseMarginsLayers(root);
+             return parseCornerRadius(root);
         } else if ("-fx-border-style".equals(prop)) {
              return parseBorderStyleLayers(root);
         } else if ("-fx-border-width".equals(prop)) {
@@ -2387,6 +2388,121 @@
         return new ParsedValueImpl<ParsedValue<ParsedValue[],Margins>[], Margins[]>(layers, Margins.SequenceConverter.getInstance());
     }
 
+
+    // http://www.w3.org/TR/css3-background/#the-border-radius
+    // <size>{1,4} [ '/' <size>{1,4}]? [',' <size>{1,4} [ '/' <size>{1,4}]?]?
+    private ParsedValueImpl<ParsedValue<ParsedValue<?,Size>[][],CornerRadii>[], CornerRadii[]> parseCornerRadius(Term root)
+            throws ParseException {
+
+
+        int nLayers = numberOfLayers(root);
+
+        Term term = root;
+        int layer = 0;
+        ParsedValueImpl<ParsedValue<?,Size>[][],CornerRadii>[] layers = new ParsedValueImpl[nLayers];
+
+        while(term != null) {
+
+            int nHorizontalTerms = 0;
+            Term temp = term;
+            while (temp != null) {
+                if (temp.token.getType() == CSSLexer.SOLIDUS) {
+                    temp = temp.nextInSeries;
+                    break;
+                }
+                nHorizontalTerms += 1;
+                temp = temp.nextInSeries;
+            };
+
+            int nVerticalTerms = 0;
+            while (temp != null) {
+                if (temp.token.getType() == CSSLexer.SOLIDUS) {
+                    error(temp, "unexpected SOLIDUS");
+                    break;
+                }
+                nVerticalTerms += 1;
+                temp = temp.nextInSeries;
+            }
+
+            if ((nHorizontalTerms == 0 || nHorizontalTerms > 4) || nVerticalTerms > 4) {
+                error(root, "expected [<length>|<percentage>]{1,4} [/ [<length>|<percentage>]{1,4}]?");
+            }
+
+            // used as index into margins[]. horizontal = 0, vertical = 1
+            int orientation = 0;
+
+            // at most, there should be four radii in the horizontal orientation and four in the vertical.
+            ParsedValueImpl<?,Size>[][] radii = new ParsedValueImpl[2][4];
+
+            ParsedValueImpl<?,Size> zero = new ParsedValueImpl<Size,Size>(new Size(0,SizeUnits.PX), null);
+            for (int r=0; r<4; r++) { radii[0][r] = zero; radii[1][r] = zero; }
+
+            int hr = 0;
+            int vr = 0;
+
+            Term lastTerm = term;
+            while ((hr <= 4) && (vr <= 4) && (term != null)) {
+
+                if (term.token.getType() == CSSLexer.SOLIDUS) {
+                    orientation += 1;
+                } else  {
+                    ParsedValueImpl<?,Size> parsedValue = parseSize(term);
+                    if (orientation == 0) {
+                        radii[orientation][hr++] = parsedValue;
+                    } else {
+                        radii[orientation][vr++] = parsedValue;
+                    }
+                }
+                lastTerm = term;
+                term = term.nextInSeries;
+            }
+
+            //
+            // http://www.w3.org/TR/css3-background/#the-border-radius
+            // The four values for each radii are given in the order top-left, top-right, bottom-right, bottom-left.
+            // If bottom-left is omitted it is the same as top-right.
+            // If bottom-right is omitted it is the same as top-left.
+            // If top-right is omitted it is the same as top-left.
+            //
+            // If there is no vertical component, then set both equally.
+            // If either is zero, then both are zero.
+            //
+
+            // if hr == 0, then there were no horizontal radii (which would be an error caught above)
+            if (hr != 0) {
+                if (hr < 2) radii[0][1] = radii[0][0]; // top-right = top-left
+                if (hr < 3) radii[0][2] = radii[0][0]; // bottom-right = top-left
+                if (hr < 4) radii[0][3] = radii[0][1]; // bottom-left = top-right
+            } else {
+                assert(false);
+            }
+
+            // if vr == 0, then there were no vertical radii
+            if (vr != 0) {
+                if (vr < 2) radii[1][1] = radii[1][0]; // top-right = top-left
+                if (vr < 3) radii[1][2] = radii[1][0]; // bottom-right = top-left
+                if (vr < 4) radii[1][3] = radii[1][1]; // bottom-left = top-right
+            } else {
+                // if no vertical, the vertical value is same as horizontal
+                radii[1][0] = radii[0][0];
+                radii[1][1] = radii[0][1];
+                radii[1][2] = radii[0][2];
+                radii[1][3] = radii[0][3];
+            }
+
+            // if either is zero, both are zero
+            if (zero.equals(radii[0][0]) || zero.equals(radii[1][0])) { radii[1][0] = radii[0][0] = zero; }
+            if (zero.equals(radii[0][1]) || zero.equals(radii[1][1])) { radii[1][1] = radii[0][1] = zero; }
+            if (zero.equals(radii[0][2]) || zero.equals(radii[1][2])) { radii[1][2] = radii[0][2] = zero; }
+            if (zero.equals(radii[0][3]) || zero.equals(radii[1][3])) { radii[1][3] = radii[0][3] = zero; }
+
+            layers[layer++] = new ParsedValueImpl<ParsedValue<?,Size>[][],CornerRadii>(radii, null);
+
+            term = nextLayer(lastTerm);
+        }
+        return new ParsedValueImpl<ParsedValue<ParsedValue<?,Size>[][],CornerRadii>[], CornerRadii[]>(layers, CornerRadiiConverter.getInstance());
+    }
+
     /* Constant for background position */
     private final static ParsedValueImpl<Size,Size> ZERO_PERCENT =
             new ParsedValueImpl<Size,Size>(new Size(0f, SizeUnits.PERCENT), null);
@@ -4374,6 +4490,9 @@
             case CSSLexer.URL:
                 break;
 
+            case CSSLexer.SOLIDUS:
+                break;
+
             default:
                 final int line = currentToken != null ? currentToken.getLine() : -1;
                 final int pos = currentToken != null ? currentToken.getOffset() : -1;
--- a/modules/graphics/src/main/java/javafx/scene/layout/Background.java	Tue Nov 05 07:57:05 2013 +1300
+++ b/modules/graphics/src/main/java/javafx/scene/layout/Background.java	Mon Nov 04 20:57:08 2013 -0500
@@ -74,10 +74,10 @@
                     PaintConverter.SequenceConverter.getInstance(),
                     new Paint[] {Color.TRANSPARENT});
 
-    static final CssMetaData<Node,Insets[]> BACKGROUND_RADIUS =
+    static final CssMetaData<Node,CornerRadii[]> BACKGROUND_RADIUS =
             new SubCssMetaData<>("-fx-background-radius",
-                    InsetsConverter.SequenceConverter.getInstance(),
-                    new Insets[] {Insets.EMPTY});
+                    CornerRadiiConverter.getInstance(),
+                    new CornerRadii[] {CornerRadii.EMPTY});
 
     static final CssMetaData<Node,Insets[]> BACKGROUND_INSETS =
             new SubCssMetaData<>("-fx-background-insets",
--- a/modules/graphics/src/main/java/javafx/scene/layout/BackgroundConverter.java	Tue Nov 05 07:57:05 2013 +1300
+++ b/modules/graphics/src/main/java/javafx/scene/layout/BackgroundConverter.java	Mon Nov 04 20:57:08 2013 -0500
@@ -62,26 +62,13 @@
             final Insets[] insets = tmp == null ? new Insets[0] : (Insets[]) tmp;
 
             tmp = convertedValues.get(Background.BACKGROUND_RADIUS);
-            final Insets[] radii = tmp == null ? new Insets[0] : (Insets[]) tmp;
+            final CornerRadii[] radii = tmp == null ? new CornerRadii[0] : (CornerRadii[]) tmp;
 
             final int lastInsetsIndex = insets.length - 1;
             final int lastRadiiIndex = radii.length - 1;
             for (int i=0; i<fills.length; i++) {
                 Insets in = insets.length > 0 ? insets[i <= lastInsetsIndex ? i : lastInsetsIndex] : Insets.EMPTY;
-                CornerRadii ra = CornerRadii.EMPTY;
-                Insets rai = radii.length > 0 ? radii[i <= lastRadiiIndex ? i : lastRadiiIndex] : null;
-                if (rai != null && rai != Insets.EMPTY) {
-                    double top = rai.getTop();
-                    double right = rai.getRight();
-                    double bottom = rai.getBottom();
-                    double left = rai.getLeft();
-                    // TODO need to write a unit test for cases where top, right, bottom, left are < 0
-                    ra = new CornerRadii(
-                            top < 0 ? 0 : top,
-                            right < 0 ? 0 : right,
-                            bottom < 0 ? 0 : bottom,
-                            left < 0 ? 0 : left, false);
-                }
+                CornerRadii ra = radii.length > 0 ? radii[i <= lastRadiiIndex ? i : lastRadiiIndex] : CornerRadii.EMPTY;
                 backgroundFills[i] = new BackgroundFill(fills[i], ra, in);
             }
         }
--- a/modules/graphics/src/main/java/javafx/scene/layout/Border.java	Tue Nov 05 07:57:05 2013 +1300
+++ b/modules/graphics/src/main/java/javafx/scene/layout/Border.java	Mon Nov 04 20:57:08 2013 -0500
@@ -96,9 +96,9 @@
             new SubCssMetaData<Margins[]> ("-fx-border-width",
                     Margins.SequenceConverter.getInstance());
 
-    static final CssMetaData<Node,Margins[]> BORDER_RADIUS =
-            new SubCssMetaData<Margins[]>("-fx-border-radius",
-                    Margins.SequenceConverter.getInstance());
+    static final CssMetaData<Node,CornerRadii[]> BORDER_RADIUS =
+            new SubCssMetaData<CornerRadii[]>("-fx-border-radius",
+                    CornerRadiiConverter.getInstance());
 
     static final CssMetaData<Node,Insets[]> BORDER_INSETS =
             new SubCssMetaData<Insets[]>("-fx-border-insets",
--- a/modules/graphics/src/main/java/javafx/scene/layout/BorderConverter.java	Tue Nov 05 07:57:05 2013 +1300
+++ b/modules/graphics/src/main/java/javafx/scene/layout/BorderConverter.java	Mon Nov 04 20:57:08 2013 -0500
@@ -87,7 +87,7 @@
             final int lastMarginIndex = borderWidths.length - 1;
 
             tmp = convertedValues.get(Border.BORDER_RADIUS);
-            final Margins[] borderRadii = tmp == null ? new Margins[0] : (Margins[]) tmp;
+            final CornerRadii[] borderRadii = tmp == null ? new CornerRadii[0] : (CornerRadii[]) tmp;
             final int lastRadiusIndex = borderRadii.length - 1;
 
             tmp = convertedValues.get(Border.BORDER_INSETS);
@@ -125,8 +125,8 @@
                 final Margins margins = borderWidths.length == 0 ?
                         null :
                         borderWidths[i <= lastMarginIndex ? i : lastMarginIndex];
-                final Margins radii = borderRadii.length == 0 ?
-                        null :
+                final CornerRadii radii = borderRadii.length == 0 ?
+                        CornerRadii.EMPTY :
                         borderRadii[i <= lastRadiusIndex ? i : lastRadiusIndex];
                 final Insets insets = borderInsets.length == 0 ?
                         null :
@@ -135,11 +135,7 @@
                 borderStrokes[i] = new BorderStroke(
                         strokes[0], strokes[1], strokes[2], strokes[3],
                         styles[0], styles[1], styles[2], styles[3],
-                        radii == null ?
-                                CornerRadii.EMPTY :
-                                new CornerRadii(
-                                    radii.getTop(), radii.getRight(), radii.getBottom(), radii.getLeft(),
-                                    radii.isProportional()),
+                        radii,
                         margins == null ?
                                 BorderStroke.DEFAULT_WIDTHS :
                                 new BorderWidths(margins.getTop(), margins.getRight(), margins.getBottom(), margins.getLeft()),
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/graphics/src/main/java/javafx/scene/layout/CornerRadiiConverter.java	Mon Nov 04 20:57:08 2013 -0500
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package javafx.scene.layout;
+
+import com.sun.javafx.css.Size;
+import com.sun.javafx.css.SizeUnits;
+import com.sun.javafx.css.StyleConverterImpl;
+import javafx.css.ParsedValue;
+import javafx.css.StyleConverter;
+import javafx.scene.text.Font;
+
+/**
+ * Convert parsed value of &lt;size&gt;{1,4} [ '/' &lt;size&gt;{1,4}]? [',' &lt;size&gt;{1,4} [ '/' &lt;size&gt;{1,4}]?]? to CornerRadii
+ */
+public final class CornerRadiiConverter extends StyleConverterImpl<ParsedValue<ParsedValue<?,Size>[][],CornerRadii>[], CornerRadii[]> {
+
+    private static final CornerRadiiConverter INSTANCE =
+            new CornerRadiiConverter();
+
+    public static CornerRadiiConverter getInstance() {
+        return INSTANCE;
+    }
+
+    // Disallow instantiation
+    private CornerRadiiConverter() { }
+
+
+    @Override
+    public CornerRadii[] convert(ParsedValue<ParsedValue<ParsedValue<?,Size>[][], CornerRadii>[], CornerRadii[]> value, Font font) {
+
+        ParsedValue<ParsedValue<?,Size>[][], CornerRadii>[] values = value.getValue();
+        CornerRadii[] cornerRadiiValues = new CornerRadii[values.length];
+
+        // parser gives us 2x4 array normalized to the rules in http://www.w3.org/TR/css3-background/#the-border-radius
+        for(int n=0; n<values.length; n++) {
+
+            ParsedValue<?,Size>[][] sizes = values[n].getValue();
+
+            Size topLeftHorizontalRadius = sizes[0][0].convert(font);
+            Size topRightHorizontalRadius = sizes[0][1].convert(font);
+            Size bottomRightHorizontalRadius = sizes[0][2].convert(font);
+            Size bottomLeftHorizontalRadius = sizes[0][3].convert(font);
+            Size topLeftVerticalRadius = sizes[1][0].convert(font);
+            Size topRightVerticalRadius = sizes[1][1].convert(font);
+            Size bottomRightVerticalRadius = sizes[1][2].convert(font);
+            Size bottomLeftVerticalRadius = sizes[1][3].convert(font);
+
+            cornerRadiiValues[n] = new CornerRadii(
+                    topLeftHorizontalRadius.pixels(),     topLeftVerticalRadius.pixels(),
+                    topRightVerticalRadius.pixels(),      topRightHorizontalRadius.pixels(),
+                    bottomRightHorizontalRadius.pixels(), bottomRightVerticalRadius.pixels(),
+                    bottomLeftVerticalRadius.pixels(),    bottomLeftHorizontalRadius.pixels(),
+                    topLeftHorizontalRadius.getUnits()     == SizeUnits.PERCENT, topLeftVerticalRadius.getUnits()      == SizeUnits.PERCENT,
+                    topRightVerticalRadius.getUnits()      == SizeUnits.PERCENT, topRightHorizontalRadius.getUnits()   == SizeUnits.PERCENT,
+                    bottomRightHorizontalRadius.getUnits() == SizeUnits.PERCENT, bottomRightVerticalRadius.getUnits()  == SizeUnits.PERCENT,
+                    bottomRightVerticalRadius.getUnits()   == SizeUnits.PERCENT, bottomLeftHorizontalRadius.getUnits() == SizeUnits.PERCENT
+            );
+
+        }
+
+        return cornerRadiiValues;
+
+    }
+}
--- a/modules/graphics/src/test/java/javafx/scene/layout/RegionCSSTest.java	Tue Nov 05 07:57:05 2013 +1300
+++ b/modules/graphics/src/test/java/javafx/scene/layout/RegionCSSTest.java	Mon Nov 04 20:57:08 2013 -0500
@@ -121,6 +121,8 @@
         scene = new Scene(region);
 
         installImage("javafx/scene/layout/red.png");
+        installImage("javafx/scene/layout/green.png");
+        installImage("javafx/scene/layout/blue.png");
         installImage("javafx/scene/layout/center-btn.png");
     }
 
@@ -329,6 +331,25 @@
         assertEquals(expected, fill);
     }
 
+    // See example 23 in http://www.w3.org/TR/css3-background/#the-border-radius
+    @Test public void testBackgroundRadiusWithHorizontalAndVerticalRadii() {
+
+        region.setStyle("-fx-background-color: black; -fx-background-radius: 2px 1px 4px / 0.5px 3px;");
+        processCSS();
+
+        assertNull(region.getBorder());
+        assertEquals(1, region.getBackground().getFills().size(), 0);
+        assertEquals(0, region.getBackground().getImages().size(), 0);
+
+        BackgroundFill fill = region.getBackground().getFills().get(0);
+        BackgroundFill expected = new BackgroundFill(Color.BLACK,
+                new CornerRadii(2, .5, 3, 1, 4, .5, 3, 1, false, false, false, false, false, false, false, false),
+                Insets.EMPTY);
+
+        assertEquals(expected, fill);
+
+    }
+
 
     /**************************************************************************
      *                                                                        *
@@ -349,9 +370,6 @@
      * in this file are checking one specific aspect of the functionality tested
      * herein.
      */
-    @Ignore ("This test fails for two reasons. First, the parser doesn't allow order independent specification of " +
-            "background-position. Second, the url has to have single quotes. It should not require this, nor should " +
-            "it complain about a missing leading /.")
     @Test public void specExample1() {
         region.setStyle(
                 "-fx-background-image: url(javafx/scene/layout/red.png), url(javafx/scene/layout/green.png), url(javafx/scene/layout/blue.png);" +
@@ -369,21 +387,21 @@
         BackgroundImage expected = new BackgroundImage(image.getImage(),
                 BackgroundRepeat.NO_REPEAT, BackgroundRepeat.NO_REPEAT,
                 new BackgroundPosition(Side.LEFT, .5, true, Side.TOP, .5, true),
-                new BackgroundSize(0, 0, false, false, false, false));
+                BackgroundSize.DEFAULT);
         assertEquals(expected, image);
 
         image = region.getBackground().getImages().get(1);
         expected = new BackgroundImage(image.getImage(),
                 BackgroundRepeat.NO_REPEAT, BackgroundRepeat.NO_REPEAT,
                 new BackgroundPosition(Side.LEFT, .2, true, Side.TOP, .8, true),
-                new BackgroundSize(0, 0, false, false, false, false));
+                BackgroundSize.DEFAULT);
         assertEquals(expected, image);
 
         image = region.getBackground().getImages().get(2);
         expected = new BackgroundImage(image.getImage(),
                 BackgroundRepeat.NO_REPEAT, BackgroundRepeat.NO_REPEAT,
                 BackgroundPosition.DEFAULT,
-                new BackgroundSize(0, 0, false, false, false, false));
+                BackgroundSize.DEFAULT);
         assertEquals(expected, image);
     }
 
@@ -2734,7 +2752,28 @@
     }
 
     // TODO Example 22
-    // TODO Example 23
+
+    // http://www.w3.org/TR/css3-background/#the-border-radius
+    // Example 23 (except using px here instead of em)
+    @Test public void testBorderRadiusWithHorizontalAndVerticalRadii() {
+
+        region.setStyle("-fx-border-color: black; -fx-border-radius: 2px 1px 4px / 0.5px 3px;");
+        processCSS();
+
+        assertNull(region.getBackground());
+        assertEquals(1, region.getBorder().getStrokes().size(), 0);
+        assertEquals(0, region.getBorder().getImages().size(), 0);
+
+        BorderStroke stroke = region.getBorder().getStrokes().get(0);
+        BorderStroke expected = new BorderStroke(Color.BLACK, BorderStrokeStyle.SOLID,
+                new CornerRadii(2, .5, 3, 1, 4, .5, 3, 1, false, false, false, false, false, false, false, false),
+                BorderStroke.DEFAULT_WIDTHS);
+
+        assertEquals(expected, stroke);
+
+    }
+
+
 
     @Test public void borderStrokeIsTransparent() {
         region.setStyle(