Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Stop opacity in JavaFX LinearGradient

I have the following SVG document:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg id="svg602"
   xmlns:svg="http://www.w3.org/2000/svg"
   xmlns="http://www.w3.org/2000/svg"
   xmlns:xlink="http://www.w3.org/1999/xlink"
   width="324.02335" height="689.724"
   space="preserve" version="1.0">

  <defs id="defs604">
    <linearGradient id="linearGradient3034">
      <stop id="stop3035" offset="0"
            style="stop-color:#0000ff;stop-opacity:1" />
      <stop id="stop3036" offset="1"
            style="stop-color:#ffffff;stop-opacity:0;" />
    </linearGradient>
    <linearGradient id="linearGradient3542"
       xlink:href="#linearGradient3034"
       x1="10" y1="10"
       x2="210" y2="210"
       gradientUnits="userSpaceOnUse" />
  </defs>

  <rect id="r1" x="10" y="10" width="200" height="200"
        style="fill:url(#linearGradient3542);stroke:none" />
</svg>

I am trying to achieve the same in JavaFX with the following code:

import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.paint.CycleMethod;
import javafx.scene.paint.LinearGradient;
import javafx.scene.paint.Stop;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

public class GradientSample extends Application {
    public static void main(String[] args) { launch(args); }

    @Override
    public void start(Stage primaryStage) {

        Group mainGroup = new Group();
        Rectangle r = new Rectangle(10, 10, 200, 200);

        Stop[] stops = new Stop[] { new Stop(0, new Color(0.0, 0.0, 1.0, 1.0)), 
                                    new Stop(1, new Color(1.0, 1.0, 1.0, 0.0)) };
        LinearGradient lg = new LinearGradient(10, 10, 210, 210, false, CycleMethod.NO_CYCLE,
                                               stops);
        r.setFill(lg);

        mainGroup.getChildren().add(r);
        Scene scene = new Scene(mainGroup, 600, 600);
        primaryStage.setScene(scene);
        primaryStage.show();
    }
}

However, the SVG is rendered (in Inkscape) like this:

enter image description here

while the JavaFX scene is rendered like this:

enter image description here

Obviously, the stop opacity (0.0) of the second stop is not considered by JavaFX (if I set the opacity to 1.0 in the SVG file, it is rendered like the JavaFX scene).

Did I miss something or should I consider this as a bug?

Is there any other solution how to achieve a Stop's opacity other than 1.0 in JavaFX?

like image 826
Andreas Fester Avatar asked Apr 07 '26 12:04

Andreas Fester


1 Answers

You have found a bug, indeed, but not in the opacity!

I've checked first that opacity was working. For instance, you can lay some label beneath the rectangle, and you will see it, so that is not the issue.

So then I exported the image as png, and edited it. I found out that opacity was well interpolated from 1 to 0, from the top left corner to the right bottom one.

To my surprise, red and green were 0 on every pixel!!

Trying to find a reported bug, I didn't find any, but I got the idea of using different rendering pipelines.

Using -Dprism.order with the options d3d (Windows), es2 (Mac) and sw, all gave the same wrong result. But the old and deprecated j2d worked fine on Windows!

To check it, I've slightly modified your code:

public void start(Stage primaryStage) {
    Group mainGroup = new Group();
    Rectangle r = new Rectangle(0, 0, 200, 200);

    Stop[] stops = new Stop[] { new Stop(0, new Color(0.0, 0.0, 1.0, 1.0)), 
                                new Stop(1, new Color(1.0, 1.0, 1.0, 0.0)) };
    LinearGradient lg = new LinearGradient(0, 0, 1, 1, true, CycleMethod.NO_CYCLE, stops);
    r.setFill(lg);

    mainGroup.getChildren().add(r);
    Scene scene = new Scene(mainGroup, 200, 200);
    scene.setFill(Color.TRANSPARENT);
    primaryStage.setScene(scene);
    primaryStage.show();

    WritableImage image = scene.snapshot(null);
    PixelReader pixelReader = image.getPixelReader();

    System.out.println("Color: " + pixelReader.getColor((int)image.getWidth()/2, (int)image.getHeight()/2));
}

Running with the regular pipelines you will see this result:

Color: 0x0000ff7f

and only running with -Dprism.order=j2d you will get:

Color: 0x7e7eff7f

As I mentioned before, opacity is interpolated, but not the RGB color: In this case it seems the RGB is the one from the first Stop.

So we may think of a regression at some point that should be reported.

like image 89
José Pereda Avatar answered Apr 09 '26 00:04

José Pereda