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.
