2 Replies Latest reply: Mar 15, 2013 4:39 PM by 796211 RSS

    Correct "clip" for background with round border

      Hi all. I'm having a problem with a round border and a round background. If you look at the code below you will see that I have a border specified with a radius of 5. If I run this, the background sticks outside of the border. If I also set the background to have a radius of 5, it still doesn't look correct as the outside of the border turns purple whereas I would expect it to be light red. If I change the background radius to 4 then the inside of the border doesn't quite match up to the background.

      How do I set a border and just say the background should be "clipped" by it?

      Thanks, Nick.
      import javafx.application.Application;
      import javafx.scene.Scene;
      import javafx.scene.control.Label;
      import javafx.scene.layout.FlowPane;
      import javafx.stage.Stage;
      public class RoundTest extends Application {
          public void start(Stage stage) throws Exception {
              FlowPane flowPane = new FlowPane();
              Scene scene = new Scene(flowPane, 500, 500);
              flowPane.setStyle("-fx-padding: 10;");
              Label label = new Label("Hello");
              label.setStyle("-fx-border-color: red; -fx-border-radius: 5; -fx-background-color: blue;");
      //        label.setStyle("-fx-border-color: red; -fx-border-radius: 5; -fx-background-color: blue; -fx-background-radius: 5;");
          public static void main(String[] args) {
        • 1. Re: Correct "clip" for background with round border
          This is actually a kind of tricky question to answer well.

          The standard caspian.css stylesheet for JavaFX 2.2 makes almost no use of borders.
          The standard caspian controls have things that look like borders or highlighted edges, but these are almost always implemented by layered backgrounds.
          For instance a focus ring around a button is a blue background which is layered underneath the button background, making the ring look like a border, even though it's not.

          I don't really know why the layered background approach was chosen over a border + background approach. Possible reasons are:
          - it's easier.
          - it performs better.
          - it provides a better visual experience.
          Maybe it is one or all of the above.

          Regardless, I think there was likely some good reason why the layered background approach was chosen, so you'd probably best stick to it.
          One advantage of using this approach is there are lots of similar examples in caspian.css that you can copy from and use.

          A sample css style string for your sample app using layered borders, might be something like:
          -fx-padding: 1;  -fx-background-insets: 0, 1;  -fx-background-radius: 5, 5;  -fx-background-color: firebrick, forestgreen;
          By playing around with combinations of layered backgrounds, padding and background-insets you can achieve, borders around your controls which can be either entirely enclosed within your control's standard layout or outside of it (like the focus ring), so that when you add something like a focus ring around your control it doesn't shift the control's onscreen position - it really just surrounds the control.

          An updated version of your sample app which uses this is below:
          import javafx.application.Application;
          import javafx.scene.*;
          import javafx.scene.control.Label;
          import javafx.scene.layout.StackPane;
          import javafx.stage.Stage;
          public class RoundTest extends Application {
            @Override public void start(Stage stage) throws Exception {
          //    System.setProperty("prism.lcdtext", "false");
              StackPane stackPane = new StackPane();
              Scene scene = new Scene(stackPane, 500, 500, true);
                  "-fx-background-color: cornsilk;"
                + " -fx-border-insets: 10;"
                + " -fx-border-color: rgba(128, 128, 128, 0.5)"
              Label label = new Label("Hello");
          //        "-fx-padding: 5;"
          //      + " -fx-background-insets: 0, 5;"
          //      + " -fx-background-radius: 5, 5;"
                  "-fx-padding: 1;"
                + " -fx-background-insets: 0, 1;"
                + " -fx-background-radius: 5, 5;"
                + " -fx-background-color: firebrick, forestgreen;"
                + " -fx-text-fill: whitesmoke;"
          //    label.setScaleX(5);
          //    label.setScaleY(5);
              stackPane.getChildren().add(new Group(label));
              scene.setCamera(new PerspectiveCamera());
            public static void main(String[] args) { launch(args); }
          Now for some other questions you raise:
          If I run this, the background sticks outside of the border
          Yeah the background radius and border radius properties are independent, so if you use a rounded border, you also need to use a rounded background or the background will overflow the border corners.
          If I also set the background to have a radius of 5, it still doesn't look correct as the outside of the border turns purple whereas I would expect it to be light red
          JavaFX 2.2 uses anti-aliasing in pretty much all of it's rendering (and even another trick for lcd called sub-pixel rendering of text in some cases). Your border is only one pixel wide. If the border isn't sitting exactly on a pixel boundary, it's going to be blended with the background. Even if the straight sides of the border sit exactly on the pixel boundary, when the border turns the corners, the anti-aliasing algorithms will kick in blending the border corner and the background around the inside edge of the corner.

          You chose a green background and a red border. When you mix those two colors, you get purple (which is what is happening).

          I don't know of anyway to enable or disable anti-aliased rendering for JavaFX scene graph objects (perhaps something to do that might come along for the ride with the 3D scene graph support in Java 8).

          Regardless, you probably want anti-aliasing engaged for your rendering anyway. So you should develop your app knowing that anti-aliasing will happen. To do this, choose colors which blend well when anti-aliased. Caspian does this by deriving a lot of the outer background colors from the inner background colors (i.e. just lightening or darkening the border). This works well because they have the same base rgb component mixture, just in different portions, so the anti-aliasing mixes them seamlessly. Choosing full colors at opposite ends of the rgb spectrum such as red and blue result in jarring mixtures like purple. Even if the anti-aliasing wasn't involved, your eyes can play tricks on you and have difficulty clearly seeing the border between these kind of colors due to the weird optics of the eyes.

          Another strategy to alleviate nasty blending is to use more pixels, e.g. instead of a one pixel border, use a two pixel border - the anti-aliasing blending will be far less noticeable. Using the greater pixel strategy works very well in conjunction with newer higher resolution screens (so called retina displays), where the pixels themselves are so small that the human eye is unable to distinguish the pixels and the way they blend.

          All that said - your original example with the rounded borders and background doesn't look too bad to me :-)


          One other issue I came across when testing this is that there is a -fx-border-style css attribute which can have values of centered, inside and outside, and when I tried to set it I got messages like:

          WARNING: com.sun.javafx.css.parser.CSSParser declaration CSS Error parsing in-line style '-fx-padding: 1; -fx-background-insets: 0, 1; -fx-background-radius: 5, 5; -fx-background-color: firebrick, forestgreen; -fx-border-style: inside; -fx-text-fill: whitesmoke;' from javafx.scene.Node$22@1bd5d8ab: Unsupported <border-style> 'inside' while parsing '-fx-border-style' at [1,138]

          The css reference guide notes that the border functionality copies from the w3c backgrounds and border functionality, but there is no inside, outside, centered border style in the w3c css reference, so I guess there is some error in the JavaFX 2 css documentation there around what is supported and what is implemented from the w3c css reference. Note that I'm not really advocating that the JavaFX css processing should match the w3c css reference as I find the w3c system complicated and difficult to understand.


          Perhaps as borders in css don't seem to be used much in JavaFX, the documentation issue isn't such a big one.
          • 2. Re: Correct "clip" for background with round border
            Wow, what an answer. Thanks so much for taking the time to write all that.

            It didn't cross my mind to use different backgrounds as you described but now you mention it, it is the way I'm going to go.

            Thanks again,