Wednesday, May 30, 2012

Dead bitmaps could be beautiful... Part II

As you saw in my last post we now have the ability to create bitmaps in JavaFX by using the Canvas node that will be part of JavaFX 2.2
That's nice...BUT...would'nt it be even nicer to have the ability to fill a shape with a pattern like the good old TexturePaint in Swing ???
And you might guess right...it will be also part of JavaFX 2.2 and is called ImagePattern


YIPPPPPEEEEEAAAAAAA


Sorry but that was needed because I was waiting for this particular feature the whole time.
For those of you that have no idea what I'm talking about let me show you a little example from the Java Swing version of the SteelSeries lib (what else :) )




The carbon texture that I use to fill the background of the gauge is just a little image of 12x12 pixels that looks like this (zoomed to 128x128 pixels)




Now I use this pattern to fill the circular shape of the background by using the TexturePaint of Java Swing.


In former JavaFX releases there was nothing like TexturePaint which means you have to create this effect on your own (which was not really effective). So with the new ImagePattern class it's really easy to create the same effect in JavaFX.


The ImagePattern class definition that i use is as follows (at the moment)


ImagePattern(Image image, double x, double y, 
             double width, double height, boolean proportional);
  • image:            the image that will be used as pattern
  • x:                   the x origin of the anchor rectangle
  • y:                   the y origin of the anchor rectangle
  • width:             the width of the anchor rectangle
  • height:           the height of the anchor rectangle
  • proportional: indicates whether start and end locations are proportional

One way to realize this is to create the pattern image in a drawing program and add it to the resources of your project, load it from there in an image and use this image as fill pattern in ImagePattern.
But because I like to create the patterns by myself during runtime i make again use of the Canvas node that i explained in my last blogpost in combination with another new feature in JavaFX 2.2...the ability to create an image of a node and again...


YIPPPPPEEEEEAAAAAAA


With this feature you could take a snapshot of a scene or node and get an image back that you might want to use to print or you might guess it already use it as a pattern for an ImagePattern object. For my example I will use the following method from the Node class


public Image snapshot(SnapshotParameters params, Image image)
  • paramsthe snapshot parameters containing attributes that will control the rendering
  • image: the image where the snapshot will be rendered to (if null a new image will created)

So i will create the carbon texture using the Canvas node with the following method


public final ImagePattern createCarbonPattern() {
    final double WIDTH        = 12;
    final double HEIGHT       = 12;
    final Canvas CANVAS       = new Canvas(WIDTH, HEIGHT);
    final GraphicsContext CTX = CANVAS.getGraphicsContext2D();

    double offsetY = 0;

    CTX.beginPath();
    CTX.rect(0, 0, WIDTH * 0.5, HEIGHT * 0.5);
    CTX.closePath();

    CTX.setFill(new LinearGradient(0, offsetY * HEIGHT,
                                   0, 0.5 * HEIGHT + offsetY * HEIGHT,
                                   false, CycleMethod.NO_CYCLE,
                                   new Stop(0, Color.rgb(35, 35, 35)),
                                   new Stop(1, Color.rgb(23, 23, 23))));
    CTX.fill();

    CTX.beginPath();
    CTX.rect(WIDTH * 0.083333, 0, WIDTH * 0.333333, HEIGHT * 0.416666);
    CTX.closePath();
    offsetY = 0;
    CTX.setFill(new LinearGradient(0, offsetY * HEIGHT,
                                   0, 0.416666 * HEIGHT + offsetY * HEIGHT,
                                   false, CycleMethod.NO_CYCLE,
                                   new Stop(0, Color.rgb(38, 38, 38)),
                                   new Stop(1, Color.rgb(30, 30, 30))));
    CTX.fill();

    CTX.beginPath();
    CTX.rect(WIDTH * 0.5, HEIGHT * 0.5, WIDTH * 0.5, HEIGHT * 0.5);
    CTX.closePath();
    offsetY = 0.5;
    CTX.setFill(new LinearGradient(0, offsetY * HEIGHT,
                                   0, 0.5 * HEIGHT + offsetY * HEIGHT,
                                   false, CycleMethod.NO_CYCLE,
                                   new Stop(0, Color.rgb(35, 35, 35)),
                                   new Stop(1, Color.rgb(23, 23, 23))));
    CTX.fill();

    CTX.beginPath();
    CTX.rect(WIDTH * 0.583333, HEIGHT * 0.5, WIDTH * 0.333333, HEIGHT * 0.416666);
    CTX.closePath();
    offsetY = 0.5;
    CTX.setFill(new LinearGradient(0, offsetY * HEIGHT,
                                   0, 0.416666 * HEIGHT + offsetY * HEIGHT,
                                   false, CycleMethod.NO_CYCLE,
                                   new Stop(0, Color.rgb(38, 38, 38)),
                                   new Stop(1, Color.rgb(30, 30, 30))));
    CTX.fill();

    CTX.beginPath();
    CTX.rect(WIDTH * 0.5, 0, WIDTH * 0.5, HEIGHT * 0.5);
    CTX.closePath();
    offsetY = 0;
    CTX.setFill(new LinearGradient(0, offsetY * HEIGHT,
                                   0, 0.5 * HEIGHT + offsetY * HEIGHT,
                                   false, CycleMethod.NO_CYCLE,
                                   new Stop(0, Color.rgb(48, 48, 48)),
                                   new Stop(1, Color.rgb(40, 40, 40))));
    CTX.fill();

    CTX.beginPath();
    CTX.rect(WIDTH * 0.583333, HEIGHT * 0.083333, WIDTH * 0.333333, HEIGHT * 0.416666);
    CTX.closePath();
    offsetY = 0.083333;
    CTX.setFill(new LinearGradient(0, offsetY * HEIGHT,
                                   0, 0.416666 * HEIGHT + offsetY * HEIGHT,
                                   false, CycleMethod.NO_CYCLE,
                                   new Stop(0, Color.rgb(53, 53, 53)),
                                   new Stop(1, Color.rgb(45, 45, 45))));
    CTX.fill();

    CTX.beginPath();
    CTX.rect(0, HEIGHT * 0.5, WIDTH * 0.5, HEIGHT * 0.5);
    CTX.closePath();
    offsetY = 0.5;
    CTX.setFill(new LinearGradient(0, offsetY * HEIGHT,
                                   0, 0.5 * HEIGHT + offsetY * HEIGHT,
                                   false, CycleMethod.NO_CYCLE,
                                   new Stop(0, Color.rgb(48, 48, 48)),
                                   new Stop(1, Color.rgb(40, 40, 40))));
    CTX.fill();

    CTX.beginPath();
    CTX.rect(WIDTH * 0.083333, HEIGHT * 0.583333, WIDTH * 0.333333, HEIGHT * 0.416666);
    CTX.closePath();
    offsetY = 0.583333;
    CTX.setFill(new LinearGradient(0, offsetY * HEIGHT,
                                   0, 0.416666 * HEIGHT + offsetY * HEIGHT,
                                   false, CycleMethod.NO_CYCLE,
                                   new Stop(0, Color.rgb(53, 53, 53)),
                                   new Stop(1, Color.rgb(45, 45, 45))));
    CTX.fill();

    final Image PATTERN_IMAGE = CANVAS.snapshot(new SnapshotParameters(), null);
    final ImagePattern PATTERN = new ImagePattern(PATTERN_IMAGE, 0, 0, WIDTH, HEIGHT, false);

    return PATTERN;
}


Most of the above code is drawing code to create the carbon look but the really interesting part are the last rows of that code.
There I create the snapshot of the CANVAS node and store it in an image named PATTERN_IMAGE. Then i take this image as image for the ImagePattern that will be returned by the method.


To fill a shape with the created ImagePattern i simply have to write the following code


Shape background = new Circle(200, 200, 160);
background.setFill(createCarbonPattern());


And the result will look similar to the following image (which is a little bit polished)




As you can see this works like a charm. I just have to say 


T H A N K   Y O U   S O   M U C H


to the JavaFX team, you really do a great job !!!


I hope you get an idea on how to use these new features in the upcoming JavaFX 2.2 release.


So keep coding...

3 comments:

  1. This comment has been removed by the author.

    ReplyDelete
  2. Really nice series of blogs, thx for writing them.

    I also created http://javafx-jira.kenai.com/browse/RT-19455 to get access to some of the texture paint functionality from css.

    ReplyDelete