VisualVM causes a crash: "EXCEPTION_ACCESS_VIOLATION" - java-7

I'm trying to use VisualVM to profile my program, but it always crashes with generally the same error message:
Waiting...
Profiler Agent: Waiting for connection on port 5140 (Protocol version: 15)
Profiler Agent: Established connection with the tool
Profiler Agent: Local accelerated session
Starting test 0
#
# A fatal error has been detected by the Java Runtime Environment:
#
# EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x00000000c12e9d20, pid=4808, tid=11472
#
# JRE version: Java(TM) SE Runtime Environment (8.0_31-b13) (build 1.8.0_31-b13)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (25.31-b07 mixed mode windows-amd64 compressed oops)
# Problematic frame:
# C 0x00000000c12e9d20
#
# Failed to write core dump. Minidumps are not enabled by default on client versions of Windows
#
# An error report file with more information is saved as:
# C:\Users\Brendon\workspace\JavaFx\hs_err_pid4808.log
Compiled method (c1) 10245 952 3 pathfinderGameTest.Pathfinder$$Lambda$4/1163157884::get$Lambda (10 bytes)
total in heap [0x00000000027d72d0,0x00000000027d7798] = 1224
relocation [0x00000000027d73f0,0x00000000027d7430] = 64
main code [0x00000000027d7440,0x00000000027d7620] = 480
stub code [0x00000000027d7620,0x00000000027d76b0] = 144
oops [0x00000000027d76b0,0x00000000027d76b8] = 8
metadata [0x00000000027d76b8,0x00000000027d76d0] = 24
scopes data [0x00000000027d76d0,0x00000000027d7730] = 96
scopes pcs [0x00000000027d7730,0x00000000027d7790] = 96
dependencies [0x00000000027d7790,0x00000000027d7798] = 8
#
# If you would like to submit a bug report, please visit:
# http://bugreport.java.com/bugreport/crash.jsp
#
Profiler Agent: JNI OnLoad Initializing...
Profiler Agent: JNI OnLoad Initialized successfully
Profiler Agent Warning: JVMTI classLoadHook: class name is null.
Profiler Agent Warning: JVMTI classLoadHook: class name is null.
Profiler Agent Warning: JVMTI classLoadHook: class name is null.
Profiler Agent Warning: JVMTI classLoadHook: class name is null.
Profiler Agent Warning: JVMTI classLoadHook: class name is null.
Profiler Agent Warning: JVMTI classLoadHook: class name is null.
Profiler Agent Warning: JVMTI classLoadHook: class name is null.
Profiler Agent Warning: JVMTI classLoadHook: class name is null.
Profiler Agent Warning: JVMTI classLoadHook: class name is null.
Profiler Agent Warning: JVMTI classLoadHook: class name is null.
Profiler Agent Warning: JVMTI classLoadHook: class name is null.
VisualVM manages to give me a very quick snapshot (~60ms), but I'm not sure how reliable such a quick test is.
I followed these instructions, but it didn't change anything. I'm using Java7 anyways, so it shouldn't even be an issue.
This is the code I'm trying to profile:
package pathfinderGameTest;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.Deque;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.function.BiConsumer;
import utils.Duple;
public class Pathfinder<T> {
//To track walls
Area<T> area;
public Pathfinder(Area<T> a) {
area = a;
}
/**
* Preset offsets representing each of the four directions.
*/
private static final List<Duple<Double>> fourDirectionOffsets = Collections.unmodifiableList(Arrays.asList(
new Duple<Double>(1.0,0.0), new Duple<Double>(-1.0,0.0), new Duple<Double>(0.0,1.0), new Duple<Double>(0.0,-1.0) ));
/**
* Finds a path from aStart to bGoal, taking into consideration walls
*
* #param aStart The start position
* #param bGoal The goal position
* #return A list representing the path from the start to the goal
*/
public List<Duple<Double>> findPathFromAToB(Duple<Double> aStart, Duple<Double> bGoal) {
Deque<Duple<Double>> frontier = new ArrayDeque<>();
Map<Duple<Double>, Duple<Double>> cameFrom = new HashMap<>();
frontier.push(aStart);
while (!frontier.isEmpty()) {
Duple<Double> current = frontier.pop();
if (current.equals(bGoal)) break;
List<Duple<Double>> neighbors = cellsAround(current, fourDirectionOffsets);
neighbors.stream()
.filter(location -> !cameFrom.containsKey(location) && area.cellIsInBounds(location) && area.getCellAt(location) == null)
.forEach( neighbor -> {
frontier.add(neighbor);
cameFrom.put(neighbor, current);
});
}
return reconstructPath(cameFrom, aStart, bGoal);
}
/**
* Transforms a backtracking map into a path
*
* #param cameFrom Backtracking map
* #param start Start position
* #param goal Goal position
* #return A list representing the path from the start to the goal
*/
private static List<Duple<Double>> reconstructPath(Map<Duple<Double>, Duple<Double>> cameFrom, Duple<Double> start, Duple<Double> goal) {
List<Duple<Double>> path = new ArrayList<>();
//path.add(goal);
Duple<Double> current = goal;
do {
if (current != goal) {
path.add(current);
}
current = cameFrom.get(current);
} while (current != null && !current.equals(start));
Collections.reverse(path);
return path;
}
/**
* Calculates the cells surrounding pos as indicated by the given offsets
* #param pos The position to find the surrounding cells of
* #param offsets Positions relative to pos to check
* #return
*/
private static List<Duple<Double>> cellsAround(Duple<Double> pos, List<Duple<Double>> offsets) {
List<Duple<Double>> surroundingCells = new ArrayList<>();
/*offsets.stream()
.map( offset -> pos.map(offset, (x1, x2) -> x1 + x2) )
.forEach(surroundingCells::add);*/
for (Duple<Double> offset : offsets) {
surroundingCells.add( pos.map(offset, (x1, x2) -> x1 + x2) );
}
return surroundingCells;
}
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
System.out.println("Waiting...");
s.nextLine();
List<Long> times = new ArrayList<>();
for (int tests = 0; tests < 900; tests++) {
System.out.println("Starting test " + tests);
long startT = new Date().getTime();
Area<Wall> a = new Area<>(500, 500);
Duple<Double> source = new Duple<>(0.0, 0.0);
Duple<Double> target = new Duple<>(500.0, 500.0);
Pathfinder<Wall> p = new Pathfinder<>(a);
List<Duple<Double>> path = p.findPathFromAToB(source, target);
times.add( (new Date().getTime()) - startT );
}
System.out.println("\n\n");
long sum = 0;
for (long t : times) {
System.out.println(t);
sum += t;
}
System.out.println("Average: " + ((double)sum / times.size() / 1000) + " seconds.");
}
}

Related

Trying to import package d3/d3.js in Angular dart.

I have moved from Java to AngularDart for a project where I have to visualise some numbers and a threat level based on data.
I decided to try to do a pie/doughnut shape with pie slices showing data properties and the centre is to show the threat level as a coloured circle.
I found an old D3Dart package and used that for my first attempt. Works fine. I can build arc's including inner- and outer radius, start- and end-angle, etc.
Reading examples on how to build I was inspired to try and move to d3/d3.js, which has (I guess) a better interface to d3.js. I need to be able to better control how I display things f.ex. replace a node element in the DOM.
When tying to import the package with import 'package:d3/d3.js' as d3;
pub serve fails with the message:
> D:\apps\Dart\dart-sdk\bin\pub.bat serve web --port=54969 Loading
> source assets... Loading angular, test/pub_serve and
> dart_to_js_script_rewriter transformers... Serving SimplePie web on
> http://localhost:54969 [web] GET Served 2 assets. [web] GET styles.css
> => SimplePie|web/styles.css Build error: Transform BuilderTransformer: Instance of 'TemplatePlaceholderBuilder' on SimplePie|primary threw
> error: Error in <unknown source>: Illegal character '949'.
>
> Error in <unknown source>: Illegal character '949'.
Last line is repeated many times.
What am I missing in my import
pubspec.yaml:
name: SimplePie
description: A web app that uses AngularDart Components
version: 0.0.1
environment:
sdk: '>=1.24.0 <2.0.0'
dependencies:
color: any
angular: ^4.0.0
angular_components: ^0.8.0
d3: ^0.2.0
dev_dependencies:
angular_test: ^1.0.0
browser: ^0.10.0
dart_to_js_script_rewriter: ^1.0.1
test: ^0.12.0
transformers:
- angular:
entry_points:
- web/main.dart
- test/**_test.dart
- test/pub_serve:
$include: test/**_test.dart
- dart_to_js_script_rewriter
Snippet of my dart code that fails when I add d3/d3.js:
import 'dart:html';
import 'dart:math' as Math;
import 'package:d3/d3.js' as d3;
import 'package:angular_components/angular_components.dart';
import 'package:angular/angular.dart';
#Component (
selector: "pie-chart",
templateUrl: 'pie_chart.html',
directives: const[CORE_DIRECTIVES, materialDirectives],
providers: const[materialProviders]
)
class SimplePie implements AfterViewInit {
#ViewChild("containerPieChart")
List<num> pieSliceList;
num numberOfSlices;
Math.Random random = new Math.Random();
final num MAX_PIE_SLICE_SIZE = 20;
num totalPieSize;
num width;
num height;
num radius;
num strokeWidth;
var progressText;
void ngAfterViewInit() {
setup();
totalPieSize = 0;
for (num i = 0 ; i < random.nextInt(10) ; i++ ) {
var value = random.nextInt(MAX_PIE_SLICE_SIZE);
pieSliceList.add(value);
totalPieSize += value;
}
numberOfSlices = pieSliceList.length;
progressText = document.getElementById("progressText");
}
void setup () {
this.width = 800;
this.height = 800;
this.strokeWidth = 3;
this.radius = (Math.min(width, height) / 2) - 2 * strokeWidth;
}
}
I use IntelliJ IDE (latest) and Dart version 1.24.3.

How can I set another value in property listener?

I want to speed-up my ScrollPane scrolling. I need something like:
scrollPane.vvalueProperty().addListener((observable, oldValue, newValue) -> {
scrollPane.setVvalue(oldValue.doubleValue() + (newValue.doubleValue() - oldValue.doubleValue()) * 2);
});
but without stackowerflow exections and working..
May be there is a way to consume this like an event?
P.S. BTW, why does setOnScroll() fire only when scrolling reaches max (top) or min (bot) position?
I don't really recommend modifying a property while it is already changing, but if you want to do it you need to set a flag to suppress recursive calls. Here's an example:
import java.util.Random;
import javafx.application.Application;
import javafx.beans.property.Property;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.Region;
import javafx.scene.layout.TilePane;
import javafx.stage.Stage;
public class ModifyScrollSpeed extends Application {
#Override
public void start(Stage primaryStage) {
ScrollPane scrollPane = new ScrollPane(createContent());
DoublingListener.register(scrollPane.vvalueProperty());
Scene scene = new Scene(scrollPane, 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
private static class DoublingListener implements ChangeListener<Number> {
private boolean doubling ;
private Property<Number> target ;
private DoublingListener(Property<Number> target) {
this.target = target ;
}
public static void register(Property<Number> target) {
target.addListener(new DoublingListener(target));
}
#Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
if (! doubling) {
doubling = true ;
target.setValue(oldValue.doubleValue() + 2 * (newValue.doubleValue() - oldValue.doubleValue()));
}
doubling = false ;
}
}
private Node createContent() {
TilePane tilePane = new TilePane();
Random rng = new Random();
for (int i = 0; i < 200; i++) {
Region region = new Region();
region.setMinSize(40, 40);
region.setPrefSize(40, 40);
region.setMaxSize(40, 40);
region.setStyle(String.format("-fx-background-color: #%02x%02x%02x;",
rng.nextInt(256), rng.nextInt(256), rng.nextInt(256)));
tilePane.getChildren().add(region);
}
return tilePane ;
}
public static void main(String[] args) {
launch(args);
}
}
I don't actually see any other way to change the increment amounts for a scroll pane. Note that ScrollBar has API for changing the increment amounts via its unitIncrement and blockIncrement properties, however ScrollPane does not have equivalent properties.
There is a comment in the source code for ScrollPane which says
/*
* TODO The unit increment and block increment variables have been
* removed from the public API. These are intended to be mapped to
* the corresponding variables of the scrollbars. However, the problem
* is that they are specified in terms of the logical corrdinate space
* of the ScrollPane (that is, [hmin..hmax] by [vmin..vmax]. This is
* incorrect. Scrolling is a user action and should properly be based
* on how much of the content is visible, not on some abstract
* coordinate space. At some later date we may add a finer-grained
* API to allow applications to control this. Meanwhile, the skin should
* set unit and block increments for the scroll bars to do something
* reasonable based on the viewport size, e.g. the block increment
* should scroll 90% of the pixel size of the viewport, and the unit
* increment should scroll 10% of the pixel size of the viewport.
*/
The current skin for the scroll pane hard codes the unit and block increments for its scroll bars (in the updateHorizontalSB and updateVerticalSB methods) in the manner described in this comment (i.e. 10% and 90% of the visible amount), so I see no real way to get at these. In Java 9 the skin class will become a public class, and so at a minimum you could subclass it and modify this behavior.
You can try something like this -
private boolean scrolllChanging = false;
private void myScroll(ObservableDoubleValue observable, Double oldValue, Double newValue) {
if (!scrollChanging) {
try {
scrollChanging = true;
// Insert logic here. Any subsequent changes won't reach here until `scrollChanging` is set to false again.
} finally {
scrollChanging = false;
}
}
}
scrollPane.vvalueProperty().addListener(this::myScroll);
Forgive any minor type errors, I have not compiled this.

What's the best practice to work with ENUM in Intersystems Caché?

Natively, Caché doesn't implement ENUMs such as Java for example, when you need to implement a solution like the following example in Java, but in Caché, what are the best practices?
public enum Planet {
MERCURY (3.303e+23, 2.4397e6),
VENUS (4.869e+24, 6.0518e6),
EARTH (5.976e+24, 6.37814e6),
MARS (6.421e+23, 3.3972e6),
JUPITER (1.9e+27, 7.1492e7),
SATURN (5.688e+26, 6.0268e7),
URANUS (8.686e+25, 2.5559e7),
NEPTUNE (1.024e+26, 2.4746e7);
private final double mass; // in kilograms
private final double radius; // in meters
Planet(double mass, double radius) {
this.mass = mass;
this.radius = radius;
}
private double mass() { return mass; }
private double radius() { return radius; }
}
final Planet mars = Planet.MARS;
Access to the code as simply Planet.MARS
Example on calculable parameters
Class so.Enum Extends %RegisteredObject
{
Parameter MERCURY As COSEXPRESSION = "..%New(3.303e+23, 2.4397e6)";
Parameter VENUS As COSEXPRESSION = "..%New(4.869e+24, 6.0518e6)";
Parameter EARTH As COSEXPRESSION = "..%New(5.976e+24, 6.37814e6)";
Parameter MARS As COSEXPRESSION = "..%New(6.421e+23, 3.3972e6)";
Parameter JUPITER As COSEXPRESSION = "..%New(1.9e+27, 7.1492e7)";
Parameter SATURN As COSEXPRESSION = "..%New(5.688e+26, 6.0268e7)";
Parameter URANUS As COSEXPRESSION = "..%New(8.686e+25, 2.5559e7)";
Parameter NEPTUNE As COSEXPRESSION = "..%New(1.024e+26, 2.4746e7)";
Property Mass As %Double;
Property Radius As %Double;
Method %OnNew(mass, radius) As %Status
{
set ..Mass=mass
set ..Radius=radius
quit $$$OK
}
}
and, you can use it so
USER>w ##class(so.Enum).#MERCURY.Mass
330300000000000000000000
USER>w ##class(so.Enum).#MERCURY.Radius
2439700
USER>w ##class(so.Enum).#MERCURY.Radius
2439700
USER>w ##class(so.Enum).#EARTH.Radius
6378140
USER>w ##class(so.Enum).#EARTH
1#so.Enum
USER>w ##class(so.Enum).#MERCURY
1#so.Enum
and you can define it as a macros
#define MERCURY ##class(so.Enum).#MERCURY
or
#define Planet(%planet) $parameter("so.Enum",$zcvt("%planet","U"))
I have never done something like this before with cache but I guess you can do something like this: (Ab)using the dynamic dispatching of propertys.
Class TST.Planet Extends %RegisteredObject
{
Property Mass As %Library.Double;
Property Radius As %Library.Double;
/// Property dispatch method to catch references to
/// virtual properties.<br>
/// This should not be called directly.<br>
Method %DispatchGetProperty(pProperty As %String) [ Final, Internal ]
{
#Dim Planets As %Library.String // multidimensional
s Planets("MERCURY")=3.303e+23_"\"_2.4397e6
s Planets("VENUS")=4.869e+24_"\"_6.0518e6
s Planets("EARTH")=5.976e+24_"\"_6.37814e6
s Planets("MARS")=6.421e+23_"\"_3.3972e6
s Planets("JUPITER")=1.9e+27_"\"_7.1492e7
s Planets("SATURN")=5.688e+26_"\"_6.0268e7
s Planets("URANUS")=8.686e+25_"\"_2.5559e7
s Planets("NEPTUNE")=1.024e+26_"\"_2.4746e7
if $DATA(Planets(pProperty)) {
s result = ##class(TST.Planet).%New()
s result.Mass=$PIECE(Planets(pProperty),"\",1)
s result.Radius=$PIECE(Planets(pProperty),"\",2)
q result
}
}
}
You then have to use it like this:
s x = ##Class(TST.Planet).%New()
s x = x.MARS
zw x
EDIT: can also do s x =##Class(TST.Planet).%New().MARS
And get this result:
x=<OBJECT REFERENCE>[2#TST.Planet]
+----------------- general information ---------------
| oref value: 2
| class name: TST.Planet
| reference count: 2
+----------------- attribute values ------------------
| Mass = 642100000000000000000000
| Radius = 3397200

How to create a Mathematica Notebook in Java?

I am looking for the prototypical 'Hello World' program that creates a Mathematica Notebook file.
I have this working program.
package graphica;
import com.wolfram.jlink.*;
/**
*
* #author Nilo
*/
public class MathematicaTester {
public static void main(String[] args) {
KernelLink ml = null;
String jLinkDir = "C:\\Program Files\\Wolfram Research\\Mathematica\\8.0\\SystemFiles\\Links\\JLink";
System.setProperty("com.wolfram.jlink.libdir", jLinkDir);
try {
ml = MathLinkFactory.createKernelLink("-linkmode launch -linkname 'C:\\Program Files\\Wolfram Research\\Mathematica\\8.0\\MathKernel.exe'");
ml.discardAnswer();
String expr = "Sum[k^2,{k,1,11}]";
ml.evaluate(expr);
ml.waitForAnswer();
String x = ml.getString();
System.out.println("Result = " + x);
} catch (MathLinkException e) {
System.out.println("Fatal error opening link: " +
e.getMessage());
return;
}
}
}
When I run this I get the following -expected- output.
run:
Result = 506
BUILD SUCCESSFUL (total time: 2 seconds)
QUESTION:
I want to change this program so that a Mathematica Notebook is created. The program will ( eventually ) add line after line of mma command strings. It would be nice if a Mathematica frontend is started at the same time and that the mma code is evaluated by request from the Java program. Essential is the creation of a Notebook that can be opened later by the mma front-end.
A method for creating a formatted notebook file is shown here:
How to create a notebook with a properly formatted expression
You can box format your Mathematica code (mathCommand) using a kernel call, e.g.
String mathCommand = "Plot[Sin[x], {x, 0, 6}]";
mathCommand = "FullForm[ToBoxes[Defer[" + mathCommand + "]]]";
MathKernel kernel = new MathKernel();
kernel.Compute(mathCommand);
mathCommand = kernel.Result.ToString();
Then encapsulate it like so, and save it with .nb extension.
Notebook[{Cell[BoxData[
... ( inserted box-formatted output ) ...
], "Input"]
},
WindowSize->{615, 750},
WindowMargins->{{328, Automatic}, {Automatic, 76}},
StyleDefinitions->"Default.nb"
]
Mathematica notebooks are plaintext files with structures like
Notebook[{Cell[],Cell[]}]
You can work out the required structure by viewing them with a text editor. Assuming you can get Java to create a text file, save it with a .nb file name ending, and invoke the command-line version of Mathematica, then what you want should be doable. You will probably want to set the input cells to initialization type.
It took some research but I managed to answer the question myself.
package graphica;
import com.wolfram.jlink.*;
/**
*
* #author Nilo
*/
public class MathematicaTester {
public static void main(String[] args) {
KernelLink ml = null;
String jLinkDir = "C:\\Program Files\\Wolfram Research\\Mathematica\\8.0\ \SystemFiles\\Links\\JLink";
System.setProperty("com.wolfram.jlink.libdir", jLinkDir);
try {
ml = MathLinkFactory.createKernelLink("-linkmode launch -linkname 'C:\\Program Files\\Wolfram Research\\Mathematica\\8.0\\MathKernel.exe'");
//test-1
ml.discardAnswer();
String expr = "Sum[k,{k,1,11}]";
ml.evaluate(expr);
ml.waitForAnswer();
String x = ml.getString();
System.out.println("Result = " + x);
//test-2
expr = "UsingFrontEnd[nb=NotebookPut[Notebook[{Cell[\"Graphics3D[Cuboid[]]\", \"Input\"]}]]]";
System.out.println("Result = " + ml.evaluateToOutputForm(expr, 40) );
expr = "UsingFrontEnd[NotebookSave[nb,\"TERRANOVA1\"]]";
System.out.println("Result = " + ml.evaluateToOutputForm(expr, 40) );
} catch (MathLinkException e) {
System.out.println("Fatal error opening link: " +
e.getMessage());
return;
}
}
}

How to make a dynamic image at run time?

I'm working on a card game based on the NetBeans platform and I'm struggling to get my head around dynamic images. Why dynamic? Well I want the cards to adjust at run time to changes to the page (i.e. name, text, cost, etc).
My first hack at it was creating a component (JPanel) with labels pre-placed where I loaded the text/image based on the card values. That seems to work fine but then it became troublesome when I thought about some pages having a different look in later editions (meaning not everything would be on the same place).
So I'm trying to get an idea about ways to do this based on some kind of template.
Any idea?
There's a follow-up question at: JList of cards?
Finally I got some time to get back to this and was able to figure out a way using Java 2D tutorial.
The pictures are not near what I will use in my application but serves as proof of concept.
package javaapplication3;
import java.awt.*; import java.awt.font.FontRenderContext; import
java.awt.font.LineBreakMeasurer; import java.awt.font.TextAttribute;
import java.awt.font.TextLayout; import java.awt.image.BufferedImage;
import java.io.File; import java.io.IOException; import
java.net.MalformedURLException; import java.net.URL; import
java.text.AttributedCharacterIterator; import
java.text.AttributedString; import java.util.ArrayList; import
java.util.HashMap; import java.util.logging.Level; import
java.util.logging.Logger; import javax.imageio.ImageIO;
/** * * #author Javier A. Ortiz Bultrón
*/ public class DefaultImageManager {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
try {
// TODO code application logic here
DefaultImageManager manager = new DefaultImageManager();
URL url = DefaultImageManager.class.getResource("weather-rain.png");
manager.getLayers().add(ImageIO.read(url));
url = DefaultImageManager.class.getResource("weather-sun.png");
manager.getLayers().add(ImageIO.read(url));
manager.addText(new Font("Arial", Font.PLAIN, 10), "Many people believe that Vincent van Gogh painted his best works "
+ "during the two-year period he spent in Provence. Here is where he "
+ "painted The Starry Night--which some consider to be his greatest "
+ "work of all. However, as his artistic brilliance reached new "
+ "heights in Provence, his physical and mental health plummeted. ",
200, 150, new Point(0, 0));
manager.generate();
} catch (MalformedURLException ex) {
Logger.getLogger(DefaultImageManager.class.getName()).log(Level.SEVERE,
null, ex);
} catch (IOException ex) {
Logger.getLogger(DefaultImageManager.class.getName()).log(Level.SEVERE,
null, ex);
}
}
/**
* Layers used to create the final image
*/
private ArrayList layers = new ArrayList();
private ArrayList textLayers = new ArrayList();
/**
* #return the layers
*/
public ArrayList<BufferedImage> getLayers() {
return layers;
}
private Dimension getMaxSize() {
int width = 0, height = 0;
for (BufferedImage img : getLayers()) {
if (img.getWidth() > width) {
width = img.getWidth();
}
if (img.getHeight() > height) {
height = img.getHeight();
}
}
return new Dimension(width, height);
}
public void addText(Font font, String text, int height, int width, Point location) {
BufferedImage textImage = new BufferedImage(width, height,
BufferedImage.TYPE_INT_ARGB);
HashMap<TextAttribute, Object> map =
new HashMap<TextAttribute, Object>();
map.put(TextAttribute.FAMILY, font.getFamily());
map.put(TextAttribute.SIZE, font.getSize());
map.put(TextAttribute.FOREGROUND, Color.BLACK);
AttributedString aString = new AttributedString(text, map);
AttributedCharacterIterator paragraph = aString.getIterator();
// index of the first character in the paragraph.
int paragraphStart = paragraph.getBeginIndex();
// index of the first character after the end of the paragraph.
int paragraphEnd = paragraph.getEndIndex();
Graphics2D graphics = textImage.createGraphics();
FontRenderContext frc = graphics.getFontRenderContext();
// The LineBreakMeasurer used to line-break the paragraph.
LineBreakMeasurer lineMeasurer = new LineBreakMeasurer(paragraph, frc);
// Set break width to width of Component.
float breakWidth = width;
float drawPosY = 0;
// Set position to the index of the first character in the paragraph.
lineMeasurer.setPosition(paragraphStart);
// Get lines until the entire paragraph has been displayed.
while (lineMeasurer.getPosition() < paragraphEnd) {
// Retrieve next layout. A cleverer program would also cache
// these layouts until the component is re-sized.
TextLayout layout = lineMeasurer.nextLayout(breakWidth);
// Compute pen x position. If the paragraph is right-to-left we
// will align the TextLayouts to the right edge of the panel.
// Note: this won't occur for the English text in this sample.
// Note: drawPosX is always where the LEFT of the text is placed.
float drawPosX = layout.isLeftToRight()
? 0 : breakWidth - layout.getAdvance();
// Move y-coordinate by the ascent of the layout.
drawPosY += layout.getAscent();
// Draw the TextLayout at (drawPosX, drawPosY).
layout.draw(graphics, drawPosX, drawPosY);
// Move y-coordinate in preparation for next layout.
drawPosY += layout.getDescent() + layout.getLeading();
}
getTextLayers().add(textImage);
}
public void generate() throws IOException {
Dimension size = getMaxSize();
BufferedImage finalImage = new BufferedImage(size.width, size.height,
BufferedImage.TYPE_INT_ARGB);
for (BufferedImage img : getLayers()) {
finalImage.createGraphics().drawImage(img,
0, 0, size.width, size.height,
0, 0, img.getWidth(null),
img.getHeight(null),
null);
}
for(BufferedImage text: getTextLayers()){
finalImage.createGraphics().drawImage(text,
0, 0, text.getWidth(), text.getHeight(),
0, 0, text.getWidth(null),
text.getHeight(null),
null);
}
File outputfile = new File("saved.png");
ImageIO.write(finalImage, "png", outputfile);
}
/**
* #return the textLayers
*/
public ArrayList<BufferedImage> getTextLayers() {
return textLayers;
}
/**
* #param textLayers the textLayers to set
*/
public void setTextLayers(ArrayList<BufferedImage> textLayers) {
this.textLayers = textLayers;
} }
It still needs some refining specially on the placement of the text but it works. I guess I can implement a xml format to store all this information so is easily configurable. In the example below suns are drawn on top of rain, and the text is on top of all that. For my application each layer will build together the page I want.
Here are the images I used:
And the final result:

Resources