Now is possible to create a tiny mall. With a few more ambients will be possible to create a entire little town.
And here the last entire tile set.
Booth, made with Gimp.
Now is possible to create a tiny mall. With a few more ambients will be possible to create a entire little town.
And here the last entire tile set.
Booth, made with Gimp.
Happy holidays in a JavaFX Style.
It’s a application to put a Santa’s hat on a picture from web. To test it obviously I used my picture with John Hall “Maddog”, the closest person I know to Santa Claus. ;D
I tried the approach from Chris Campbell’s Effect Playground application but I needed get both photo image and hat at the same time. In this application I used the Jean-Francois Screenshot Maker approach, taking a screenshot of the desired area. Maybe not the best solution, but it works very well. I also used his Screencapture.java and Util.java codes.
package santahat; import javafx.ext.swing.SwingTextField; import javafx.scene.CustomNode; import javafx.scene.input.MouseEvent; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.layout.VBox; import javafx.scene.layout.HBox; import javafx.scene.Node; import javafx.scene.Scene; import javafx.stage.Stage; import javafx.ext.swing.SwingButton; var imgview = ImageView { image: Image { url: "{__DIR__}help.png" } } var txtfield:SwingTextField = SwingTextField { columns: 45 text: "your image url" editable: true action: function(){ println(txtfield.text); imgview.image = Image { url: txtfield.text; } } }; var santahat:ImageView = ImageView { x: 240 y: -5 var startX = 0.0 var startY = 0.0 var zoom = 1.0 var angle = 0.0 scaleX: bind zoom scaleY: bind zoom rotate: bind angle onMousePressed: function( e: MouseEvent ):Void { startX = e.sceneX - santahat.translateX; startY = e.sceneY - santahat.translateY; } onMouseDragged: function( e: MouseEvent ):Void { santahat.translateX = e.sceneX - startX; santahat.translateY = e.sceneY - startY; } onMouseWheelMoved: function( e: MouseEvent ):Void { if(e.controlDown) { angle += e.wheelRotation * 10; } else { zoom += e.wheelRotation / 20; } } image: Image { url: "{__DIR__}santahat.png" } } // Based on the Jean's ScreenshotMaker // http://javafx.com/samples/ScreenshotMaker/index.html var stage:Stage = Stage { title: "Santa Hat" width: 510 height: 480 scene: Scene { content: [ VBox { content: [ txtfield, imgview, SwingButton { text: "Save" action: function() { // Ugly. See the entire source at through the link in the blog post } }] }, santahat] } } |
Downloads:
For those who uses Eclipse IDE: there’s already a JavaFX plugin for you. Check out here. To try out, Paul Bakker wrote a post introduction showing how to create a cool first application.
Is a another big free organism in our JavaFX ecosystem!
Changelog:
Gravatar is easy way to put global recognized avatar images into any Internet application. Gravatar would stands for globally recognized avatar.
Below, the Java class that I got from the Gravatar Java reference. Here is a static class called md5 that applies a MD5Sum algorithm over a string. Is a little complex code but all behavior keeps encapsulated and who uses it don’t need to know how it works. Just gives a string and receives a encrypted string. Those two codes are also a good example of how calling Java classes inside a JavaFX code.
package gravatarexample; import java.security.MessageDigest; import java.io.UnsupportedEncodingException; import java.security.NoSuchAlgorithmException; public class MD5 { public static String toHex(String message) { try { MessageDigest md = MessageDigest.getInstance("MD5"); byte array[] = md.digest(message.getBytes("CP1252")); StringBuffer sb = new StringBuffer(); for (int i = 0; i < array.length; ++i) { sb.append(Integer.toHexString((array[i]&0xFF)|0x100).substring(1, 3)); } return sb.toString(); } catch (NoSuchAlgorithmException e) { } catch (UnsupportedEncodingException e) { } return null; } } |
As a Java class in the same package, any JavaFX (or Java) code can call it without any problem. Just to keep the code more clear I’m importing it explicitly. Is this example I also create some Swing interface to give user the option to put his mail, adjust the image size and get a output direct link or html image tag.
package gravatarexample; import gravatarexample.MD5; import javafx.ext.swing.SwingButton; import javafx.ext.swing.SwingSlider; import javafx.ext.swing.SwingTextField; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.layout.VBox; import javafx.scene.Scene; import javafx.stage.Stage; var mail = "Email"; var key = ""; function gravatalize(mail:String, size: Integer): String { return "http://www.gravatar.com/avatar/{MD5.toHex(mail)}?s={size}" } var inputtxt = SwingTextField { columns: 20 text: mail } var slider = SwingSlider { minimum: 10 maximum: 128 value: 100 vertical: false } var button = SwingButton { text: "Get Gravatar" action: function() { key = gravatalize(inputtxt.text, slider.value); directoutput.text = key; htmloutput.text = "<img src="{key}" alt="\"gravatar\"" />"; photo.image = Image { backgroundLoading: true, url: key}; } } var photo:ImageView = ImageView { image: null } var directoutput = SwingTextField { columns: 20 text: "direct link image" } var htmloutput = SwingTextField { columns: 20 text: "html tag image" } Stage { title: "Gravatar" width: 300 height: 340 scene: Scene { content: [ VBox { spacing: 10 content: [inputtxt, slider, button, directoutput, htmloutput, photo] }, ] } } |
The string itself is assembled in the gravatalize function. You give a mail and it’s returns a Gravatar direct link to the image. There’s many cool ways to use together Gravatar and a JavaFX Internet application.
Tile sets are a very simple way to draw scenarios with repeated elements. From simple to complex ones using a very low footprint.
First step, load the png file that stores the tileset into a Image. The file tiles.png shoud be in the same directory of the source code. I adjusted some tiles from those tile set I’ve blogged here before into a grid of 10×10 tiles.
var tileset = Image { url: "{__DIR__}tiles.png" } |
Notice that each tile have 32 of height and 32 of width. We will assume this and use theses numbers when performing calculations to find a single tile in our tile set.
def w = 32; def h = 32; |
To display a Image in the screen we use a ImageView node. A ImageView can have a viewport property to create crop or zoom effect. A viewport is just a Rectangle2D, a object with position (minX and minY), height and width. If we want to display the first tile in the tileset we do
ImageView { image: tileset viewport: Rectangle2D{ minX: 0, minY: 0, height: 32, width: 32 } } |
Notice that the minX determines the column and minY the row in the tileset. The first row is 0*32, the second row is 1*32 and so on. If we want to display the tile at the second line and third column of the tileset we do
ImageView { image: tileset viewport: Rectangle2D{ minX: 2 * 32 , minY: 1*32, height: 32, width: 32 } } |
Those properties in a Rectangle2D are for init and read only. So I created a list with all Rectangles I can need for use as a viewport.
def viewports = for (row in [0..9]) { for (col in [0..9]) { Rectangle2D{ minX: col * w, minY: row * h, height: w, width: h } } } |
The scenario map is stored in another list. The first element of the list is 7, that is, the first tile in the scenario is the 7th tile from the tile set.
var map = [ 7, 3, 3, 3, 3, 3, 3, 3, 3, 8, 19, 26, 40, 41, 24, 13, 13, 23, 24, 19, 19, 36, 50, 51, 34, 2, 2, 2, 34, 19, 19, 2, 2, 2, 2, 2, 2, 2, 25, 19, 19, 57, 58, 44, 45, 46, 2, 2, 35, 19, 27, 3, 3, 6, 55, 56, 5, 3, 3, 38, 19, 60, 13, 16, 47, 48, 15, 13, 61, 19, 19, 70, 1, 33, 1, 1, 1, 1, 71, 19, 19, 1, 1, 1, 1, 1, 1, 1, 49, 19, 17, 9, 9, 9, 9, 9, 9, 9, 9, 18, ]; |
Finally to create a scenario with 100 tiles, 10 per row and with 10 rows, in a list called tiles. Each iteration of this loop creates a ImageView. Each ImageView will store a single tile. We get the tile number in the map list and so use it to index the viewports list.
var tiles = for (row in [0..9]) { for (col in [0..9]) { ImageView { x: col * w, y: row * h, viewport: bind viewports[map[row * 10 + col]] image: tileset } } } |
Additionally I added two things to transform this program also in a (extremely)Â simple map editor. At each ImageView I added a callback for onMouseClicked event. When you click on a tile, it changes its map position, ie, the tile. The next tile for the left button and the last tile for any other button.
onMouseClicked: function( e: MouseEvent ):Void { var amount = if(e.button == MouseButton.PRIMARY) { 1 } else { -1 }; map[row * 10 + col] = (map[row * 10 + col] + amount) mod 100; } |
The other thing is to print the map list when the program is over. There is the full program:
package tileeditor; import javafx.stage.Stage; import javafx.scene.Scene; import javafx.scene.image.ImageView; import javafx.scene.image.Image; import javafx.scene.CustomNode; import javafx.scene.Group; import javafx.scene.Node; import javafx.geometry.Rectangle2D; import javafx.scene.input.MouseEvent; import javafx.scene.input.MouseButton; def w = 32; def h = 32; var map = [ 7, 3, 3, 3, 3, 3, 3, 3, 3, 8, 19, 26, 40, 41, 24, 13, 13, 23, 24, 19, 19, 36, 50, 51, 34, 2, 2, 2, 34, 19, 19, 2, 2, 2, 2, 2, 2, 2, 25, 19, 19, 57, 58, 44, 45, 46, 2, 2, 35, 19, 27, 3, 3, 6, 55, 56, 5, 3, 3, 38, 19, 60, 13, 16, 47, 48, 15, 13, 61, 19, 19, 70, 1, 33, 1, 1, 1, 1, 71, 19, 19, 1, 1, 1, 1, 1, 1, 1, 49, 19, 17, 9, 9, 9, 9, 9, 9, 9, 9, 18, ]; var tileset = Image { url: "{__DIR__}tiles.png" } def viewports = for (row in [0..9]) { for (col in [0..9]) { Rectangle2D{ minX: col * w, minY: row * h, height: w, width: h } } } var tiles = for (row in [0..9]) { for (col in [0..9]) { ImageView { x: col * w, y: row * h, viewport: bind viewports[map[row * 10 + col]] image: tileset onMouseClicked: function( e: MouseEvent ):Void { var amount = if(e.button == MouseButton.PRIMARY) { 1 } else { -1 }; map[row * 10 + col] = (map[row * 10 + col] + amount) mod 100; } } } } Stage { title: "JavaFX Simple Tile Editor" scene: Scene { content: [ tiles ] } onClose: function() { println(map); } } |
Here is the result for that map
And you can try it yourself in your browser. Play it online now.
Here is a video of it working
Downloads:
Possibilities
We are using just a image that can handle 100 tiles, tiles.png with less than 30Kb. The map is also composed with 100 tiles. Each tile we can choose between 100 different tiles, so we can compose 10100 different maps (one googol10 ). Most of them are useless and without any sense, but some are cool. :)