As stated in my last article, I tried to render the writing part off screen to create an image off of it and then deform that.
That’s what I did with this result:
Turn’s out that this is almost the same result as when applying the lines to a group with a transparent background and then transform it:
However for completeness sake here is the code for the off screen example. It seams that the margins at the left and top look a bit better:
package ch.sahits.javafx.test.stretch; import ch.sahits.javafx.test.ResizeableCanvas; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; import javafx.application.Application; import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.control.Label; import javafx.scene.control.LabelBuilder; import javafx.scene.effect.PerspectiveTransform; import javafx.scene.effect.PerspectiveTransformBuilder; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.image.WritableImage; import javafx.scene.paint.Color; import javafx.scene.shape.Rectangle; import javafx.scene.shape.RectangleBuilder; import javafx.stage.Stage; public class OffScreenImageGeneration extends Application { @Override public void start(Stage primaryStage) { try { Group root = new Group(); Image img = new Image(getClass().getResource("PaperScroll.png").openStream()); final double width = img.getWidth(); final double height = img.getHeight(); Scene scene = new Scene(root, width, height); ImageView imageView = new ImageView(img); WritableImage image = createOffScreenImage(); PerspectiveTransform perspectiveTransform = PerspectiveTransformBuilder.create() .ulx(200).uly(0) .urx(544).ury(19) .llx(0).lly(403) .lrx(559).lry(502) .build(); ImageView imageView2 = new ImageView(image); imageView2.setTranslateX(366); imageView2.setTranslateY(29); imageView2.setEffect(perspectiveTransform); root.getChildren().addAll(imageView, imageView2); primaryStage.setTitle("Perspective Transform"); primaryStage.setScene(scene); primaryStage.show(); } catch (IOException ex) { Logger.getLogger(ResizeableCanvas.class.getName()).log(Level.SEVERE, null, ex); } } public static void main(String[] args) { launch(args); } private WritableImage createOffScreenImage() { Group offScreenRoot = new Group(); Scene offScreen = new Scene(offScreenRoot, 559, 502); RectangleBuilder rectBuilder = RectangleBuilder.create() .opacity(0.5) .fill(Color.RED) .width(559) .height(502); Rectangle untransformed = rectBuilder.build(); Label line1 = LabelBuilder.create() .text("First Line") .style("-fx-font-size: 24px") .translateX(10) .translateY(20) //.effect(perspectiveTransform) .build(); Label line2 = LabelBuilder.create() .text("Second Line") .style("-fx-font-size: 24px") .translateX(10) .translateY(50) //.effect(perspectiveTransform) .build(); offScreenRoot.getChildren().addAll(untransformed, line1, line2); WritableImage image = new WritableImage((int)offScreen.getWidth(), (int)offScreen.getHeight()); offScreen.snapshot(image); return image; } }
All the heavy work is done by the createOffScreenImage method, where a second scene is created with a group of the exact measures of the rectangle to be deformed.
And here the other code:
package ch.sahits.javafx.test.stretch; import ch.sahits.javafx.test.ResizeableCanvas; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; import javafx.application.Application; import javafx.scene.Group; import javafx.scene.GroupBuilder; import javafx.scene.Scene; import javafx.scene.control.Label; import javafx.scene.control.LabelBuilder; import javafx.scene.effect.PerspectiveTransform; import javafx.scene.effect.PerspectiveTransformBuilder; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.paint.Color; import javafx.scene.shape.Rectangle; import javafx.scene.shape.RectangleBuilder; import javafx.stage.Stage; public class PerpectiveTransformEffectTest extends Application { @Override public void start(Stage primaryStage) { try { Group root = new Group(); Image img = new Image(getClass().getResource("PaperScroll.png").openStream()); final double width = img.getWidth(); final double height = img.getHeight(); Scene scene = new Scene(root, width, height); ImageView imageView = new ImageView(img); RectangleBuilder rectBuilder = RectangleBuilder.create() .translateX(366) .translateY(29) .opacity(0.5) .fill(Color.RED) .width(559) .height(502); PerspectiveTransform perspectiveTransform = PerspectiveTransformBuilder.create() .ulx(200).uly(0) .urx(544).ury(19) .llx(0).lly(403) .lrx(559).lry(502) .build(); Label line1 = LabelBuilder.create() .text("First Line") .style("-fx-font-size: 24px") .translateX(20) .translateY(40) .build(); Label line2 = LabelBuilder.create() .text("Second Line") .style("-fx-font-size: 24px") .translateX(20) .translateY(70) .build(); Rectangle transparent = rectBuilder.build(); transparent.setOpacity(0); Group text = GroupBuilder.create() .translateX(366) .translateY(29) .children(transparent, line1, line2) .effect(perspectiveTransform) .build(); root.getChildren().addAll(imageView, text); primaryStage.setTitle("Perspective Transform"); primaryStage.setScene(scene); primaryStage.show(); } catch (IOException ex) { Logger.getLogger(ResizeableCanvas.class.getName()).log(Level.SEVERE, null, ex); } } public static void main(String[] args) { launch(args); } }
Which of the two are better is hard to say, however I would guess that the one with the off screen image would be better when thinking about scaling the scene.