javajavafx-2javafx

How to create a JavaFX texture


I can make a rectangle with JavFX like this:

Rectangle node2 = RectangleBuilder.create()
            .x(-100)
            .y(-100)
            .width(200)
            .height(200)
            .fill(Color.GREEN)
            .build();

but how can I make it use a texture instead of just a color?

TIA


Solution

  • Update 2024

    The original answer was written many years ago and uses a Builder pattern which was removed from the JavaFX API.

    Additionally, new features have been added to the JavaFX API over the years, offering more options.

    So I have updated this answer with new information.

    Regardless, if you find this question you are probably looking for an ImagePattern to apply to a 2D shape, as the original asker was looking for.

    Rectangle patternWithNoRepeats = new Rectangle(
            image.getWidth(), image.getHeight(),
            new ImagePattern(image)
    );
    

    2D Textures

    2D "textures" are Paint.

    There are various kinds of paint, which can be seen from the subclasses of paint:

    Anything that accepts paint as an argument (e.g. fills and strokes of shapes, backgrounds of regions, etc) can use any of the paint types mentioned above.

    3D Textures

    3D textures are used in Material. The only material provided in JavaFX 23 is a PhongMaterial. The javadoc for this class is good, and I encourage you to read it if you intend to do any 3D work.

    The basic way to map a 2D image onto a 3D object is to apply the Image as a diffuseMap.

    A TriangleMesh can project portions of the 2D diffuse image onto a 3D model using uv coordinates, as explained in:

    Materials are based on images and colors and can encode additional information to supplement a flat image, such as bump maps, spectral highlights, self-illumination, etc.

    2D Texture example

    This example demonstrates using textures by:

    dragon screenshot

    TextureApp.java

    import javafx.application.Application;
    import javafx.geometry.Insets;
    import javafx.geometry.Pos;
    import javafx.scene.Scene;
    import javafx.scene.layout.Background;
    import javafx.scene.layout.HBox;
    import javafx.scene.paint.*;
    import javafx.scene.shape.Rectangle;
    import javafx.stage.Stage;
    import javafx.scene.image.Image;
    
    import java.util.Objects;
    
    public class TextureApp extends Application {
        private static final String IMAGE_FILE = "Dragon-icon.png";
        private static final double NUM_REPEATS = 2;
    
        @Override
        public void start(Stage stage) {
            Image image = new Image(
                    getResourceURL(IMAGE_FILE)
            );
    
            Rectangle patternWithNoRepeats = new Rectangle(
                    image.getWidth(), image.getHeight(),
                    new ImagePattern(image)
            );
            patternWithNoRepeats.setStroke(Color.PALEGREEN);
            patternWithNoRepeats.setStrokeWidth(4);
    
            Rectangle patternWithRepeats = new Rectangle(
                    image.getWidth() * NUM_REPEATS, image.getHeight() * NUM_REPEATS,
                    new ImagePattern(image, 0, 0, 1 / NUM_REPEATS, 1 / NUM_REPEATS, true)
            );
            patternWithRepeats.setStroke(Color.PALETURQUOISE);
            patternWithRepeats.setStrokeWidth(4);
    
            HBox layout = new HBox(10, patternWithNoRepeats, patternWithRepeats);
            layout.setAlignment(Pos.CENTER);
            layout.setPadding(new Insets(10));
            layout.setBackground(
                    Background.fill(
                            new LinearGradient(
                                    0, 0, 0, 1,
                                    true,
                                    CycleMethod.NO_CYCLE,
                                    new Stop[] {
                                            new Stop(0, Color.CORAL),
                                            new Stop(1, Color.CORNFLOWERBLUE)
                                    }
                            )
                    )
            );
    
            stage.setScene(new Scene(layout));
            stage.show();
        }
    
        private static String getResourceURL(String filename) {
            return Objects.requireNonNull(
                    TextureApp.class.getResource(
                            filename
                    )
            ).toExternalForm();
        }
    }
    

    Dragon-icon.png

    dragon icon


    Original Answer

    Set the fill to an ImagePattern

    Rectangle node2 = 
      RectangleBuilder.create()
        .x(-100)
        .y(-100)
        .width(200)
        .height(200)
        .fill(
          new ImagePattern(
            new Image("file:flower.png"), 0, 0, 1, 1, true
          )
        )
        .build();
    

    There are additional samples in the ImagePattern javadoc.

    For JavaFX8, you will also be able to do this via css.