In one of my projects I use JUNG2 to visualize a very large multiple-parent hierarchy graph, displayed in an applet. I would need to export the whole/parts of the graph to high resolution still images, since screenshots look hideous when printed (especially if the graph has been zoomed out).
The code I use currently is as follows:
public void writeToDisk(File saveToFolder, String filename) {
//Dimension loDims = getGraphLayout().getSize();
Dimension vsDims = getSize();
int width = vsDims.width;
int height = vsDims.height;
Color bg = getBackground();
BufferedImage im = new BufferedImage(width,height,BufferedImage.TYPE_INT_BGR);
Graphics2D graphics = im.createGraphics();
graphics.setColor(bg);
graphics.fillRect(0,0, width, height);
paintComponent(graphics);
try{
ImageIO.write(im,"png",new File(saveToFolder,filename));
}catch(Exception e){
e.printStackTrace();
}
}
This creates PNG images which are not particularly high resolution. So my questions are as follows:
Is it possible to push up the PNG export resolution to 300 dpi?
Is it possible to export the graph, or any swing component for that matter, to vector based formats such as EPS, PDF or SVG without too much hassle? I have found several libraries (VectorGraphics2D,FreeHEP) for managing vector based images in Java, however I am not sure if using them would mean that I have to "re-draw" each vertex and edge in the graph. That's obviously not very desirable...
Are there any other alternatives which I might have missed?
Thanks in advance,
Thanks for the suggestions but I have managed to get FreeHEP Vector Graphics library working the way I want to. I am sharing the code below in case anyone runs into the same questions.
The above-named library has a very nice built-in export menu, which handles the export to a bunch of different formats. Code excerpt from the modified ´ModelGraphMouse´ class:
protected void handlePopup(MouseEvent e) {
final VisualizationViewer<MyNode, MyEdge> vv = (VisualizationViewer<MyNode, MyEdge>)e.getSource();
Point2D p = e.getPoint();
GraphElementAccessor<MyNode, MyEdge> pickSupport = vv.getPickSupport();
if(pickSupport != null) {
final MyNode v = pickSupport.getVertex(vv.getGraphLayout(), p.getX(), p.getY());
// if clicked on a vertex -> show info popup
// else show contexual menu
if(v != null) {
JFrame popup = new JFrame("Node: " + v.getId());
popup.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
...
} else{
JPopupMenu menu = new JPopupMenu();
JMenuItem exportGraphMenuItem = new JMenuItem("Export graph to vector image...");
exportGraphMenuItem.addActionListener(new ExportActionListener((WritingVisualizationViewer<V, E>) vv));
menu.add(exportGraphMenuItem);
menu.show(e.getComponent(), e.getX(), e.getY());
}
}
}
and the action listener:
public class ExportActionListener implements ActionListener{
private VisualizationViewer<V, E> wvv;
public ExportActionListener(VisualizationViewer<V, E> vv) {
this.wvv = vv;
}
#Override
public void actionPerformed(ActionEvent e) {
ExportDialog export = new ExportDialog();
export.showExportDialog(wvv, "Export view as ...", wvv, "export");
}
}
Basically a PNG suffices. The dimension of resolution in a BufferedImage is pixels, not dpi. So you need to double/triple your width and height to receive a better resolution.
Graphics2D could scale too for the JUNG graphs.
You might wanna use Batik for that : http://xmlgraphics.apache.org/batik/using/svg-generator.html
you can use Xchart and then export pictures using vectorgraphics2d to SVG or PDF
Related
I've got a Java desktop app that works, amongst other, on OS X.
Now the new MacBook Pro has a retina display and I'm concerned: how is it going to work regarding Swing?
What about when a Java app uses both Swing components and some bitmap graphics (like custom icons / ImageIcon)?
Shall all desktop Java apps be automatically resized (for example by quadrupling every pixel) or am I going to need to create two versions of my icons set (for example one with 24x24 icons and the other with 96x96 icons) and somehow determine that the app is running on a retina display?
Use IconLoader library. It supports HiDPI images http://bulenkov.com/iconloader/ It also provides a way to work with HiDPI images (drawing, etc)
On Apple's Java 6 you can provide multiple versions of the same image. Depending on the screen (retina or not), one or the other image is picked and drawn.
However, those images have to loaded in a special way:
Toolkit.getDefaultToolkit().getImage("NSImage://your_image_name_without_extension");
For example, if your (regular resolution) image is called: "scissor.png", you have to create a high resolution version "scissor#2x.png" (following the Apple naming conventions) and place both images in the Resources directory of your app bundle (yes, you need to bundle your app).
Then call:
Image img = Toolkit.getDefaultToolkit().getImage("NSImage://scissor");
You can use the resulting image in your buttons and it will be drawn with the right resolution magically.
There are two other "tricks" you can use:
Using an AffineTransform of (0.5, 0.5) on your Graphics2D object before drawing an Image. Also see this java-dev message
Creating a high dpi version of your image programmatically using this hack
The first "trick" (0.5 scaling) by now also works on Oracle's Java 7/8.
I.e. if you draw an image with 0.5 scaling directly to the component's Graphics object, it will be rendered in high resolution on Retina displays (and also with half its original size).
Update
Starting with Java 9, there is better built-in support for images with different resolutions via the MultiResolutionImage interface. For more details, please see this answer.
I can confirm that the scaling your images works with on Oracle Java 1.8. I cannot get the NSImage hack to work on java 1.7 or 1.8. I think this only works with Java 6 from Mac...
Unless someone else has a better solution, what I do is the following:
Create two sets of icons.
If you have a 48pixel width icon create one 48px #normal DPI and another at 96px with 2x DPI. Rename the 2xDPI image as #2x.png to conform with apple naming standards.
Subclass ImageIcon and call it RetinaIcon or whatever.
You can test for a Retina display as follows:
public static boolean isRetina() {
boolean isRetina = false;
GraphicsDevice graphicsDevice = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
try {
Field field = graphicsDevice.getClass().getDeclaredField("scale");
if (field != null) {
field.setAccessible(true);
Object scale = field.get(graphicsDevice);
if(scale instanceof Integer && ((Integer) scale).intValue() == 2) {
isRetina = true;
}
}
}
catch (Exception e) {
e.printStackTrace();
}
return isRetina;
}
Make sure to #Override the width and height of the new ImageIcon class as follows:
#Override
public int getIconWidth()
{
if(isRetina())
{
return super.getIconWidth()/2;
}
return super.getIconWidth();
}
#Override
public int getIconHeight()
{
if(isRetina())
{
return super.getIconHeight()/2;
}
return super.getIconHeight();
}
Once you have a test for the retina screen and your custom width/height methods overridden you can customise the painIcon method as follows:
#Override
public synchronized void paintIcon(Component c, Graphics g, int x, int y)
{
ImageObserver observer = getImageObserver();
if (observer == null)
{
observer = c;
}
Image image = getImage();
int width = image.getWidth(observer);
int height = image.getHeight(observer);
final Graphics2D g2d = (Graphics2D)g.create(x, y, width, height);
if(isRetina())
{
g2d.scale(0.5, 0.5);
}
else
{
}
g2d.drawImage(image, 0, 0, observer);
g2d.scale(1, 1);
g2d.dispose();
}
I do not know how this will work with multiple screens though- is there anyone else that can help out with that???
Hope this code helps out anyway!
Jason Barraclough.
Here is an example of using the scaling as mentioned above:
RetinaIcon is on the left. ImageIcon is on the right
Here is a solution, that works also when the icons are used in the apple menu. There the icon is automatically greyed. So I have implemented a class DenseIcon which paints densely:
public synchronized void paintIcon(Component c, Graphics g, int x, int y) {
if(getImageObserver() == null) {
g.drawImage(getImage0(), x, y, getIconWidth(), getIconHeight(), c);
} else {
g.drawImage(getImage0(), x, y, getIconWidth(), getIconHeight(), getImageObserver());
}
}
How to hook into the greying I have not yet figured out. So as a kludge we return a low res image so that the menu can do its modifications:
public Image getImage() {
Image image = getImage0().getScaledInstance(
getIconWidth(),
getIconHeight(),
Image.SCALE_SMOOTH);
ImageIcon icon = new ImageIcon(image, getDescription());
return icon.getImage();
}
You find the code of the full class here on gist. You need to instantiate the icon class with an URL to an image that is twice the size. Works for 2K displays.
This how icons look like on my retina macbook '12:
On the left side icons in IntelliJ IDEA 11 (swing app) and on the right side IDEA 12 which is claimed to be retinized. As you can see automatically resized icons (on the left) looks pretty ugly.
As far as I know, they, just like the guys from Chrome team, made it by providing double sized icons.
Dear wonderful people of stackoverflow
A group of my friends are attempting to make a level editor in Java.
We have a Jpanel instead of a Jframe and we are trying to put small images onto the Jpanel from a filepath saved as a string. In the end we want a list of images that you can just drop on. So far we have tried a few methods with no luck.
We can load the images, however we can't get these images to actually display, What would be the best means of solving said problem?
below is a sample of what we have so far.
EnemyPlacementGrid = new JPanel();
EnemyPlacementGrid.addMouseListener(new MouseAdapter() {
//#Override
public int mouseX;
public int mouseY;
public void mouseClicked(MouseEvent arg0) { //what happens when you click in the EnemyPlacementGrid
System.out.println("Correct Area for placement");
mouseX = arg0.getX();
mouseY = arg0.getY();
//System.out.println("X:" + mouseX + ", Y:" + mouseY );
Enemy newEnemy = workingEnemy.cloneSelf();
newEnemy.setLocation(mouseX, mouseY);
System.out.println("newEnemy object: " + newEnemy);
System.out.println(newEnemy.weaponList);
currentWave.addEnemy(newEnemy);
System.out.print(currentLevel);
}
});
Any and all help is greatly appreciated.
UPDATE:
As of now I have an image appearing, however I can't update said image. Note code below:
public void run() {
try {
BufferedImage img = ImageIO.read(new File(IMG_PATH));
ImageIcon icon = new ImageIcon(img);
WaveScreen frame = new WaveScreen();
JPanel panel = (JPanel)frame.getContentPane();
JLabel label = new JLabel();
label.setIcon(new ImageIcon("images/map_on.png"));// your image here
panel.add(label);
frame.setVisible(true);
panel.add(label);
panel.repaint();
} catch (Exception e) {
e.printStackTrace();
}
update, method tried from comments:
Graphics2D g = null;
Graphics2D g2 = (Graphics2D)g;
Image imageVariable = new ImageIcon("images/map_on.png").getImage();
g.drawImage(imageVariable, mouseX, mouseY, null);
Well, i'd say to try using Graphics, meaning you need to override the paint method; i'd recommend that you put the mouseX and mouseY as global variables though…
// creating global image variable for use later
Image imageVariable = new ImageIcon("image path").getImage();
public void paintComponent(Graphics g) {
// here you could either create a Graphics2D object
// Graphics2D g2 = (Graphics2D)g;
// or you could use the g parameter as it is, doesn't matter.
// use the global variable for the image to be drawn onto the screen
// use the global value of the mouseX and mouseY for where you click the mouse
// to place the image, and this should be it
g.drawImage(imageVariable, mouseX, mouseY, null);
}
Hope this helps!
If the game is simple, user2277872's solution will work and you can use graphics2D from java. However, if you are planning on a more sophisticated game (lots of interaction, lots of textures), then the default Java framework for 2D graphics will prove to be too slow.
If you are planning on such a game, I can highly recommend either learning OpenGL or using an existing framework for graphics, such as
JMonkeyEngine (http://jmonkeyengine.com/)
or
Slick (http://slick.cokeandcode.com/index.php)
More information: What should I use to display game graphics?
I am a beginner who is learning to write games in JAVA.
In the game I am writing, I am trying to get it to support multiple displayModes. First let me tell you a little about how I'm setting the display setting in the first place.
In the beginning of the code, I have an list of display modes I wish to support
//List of supported display modes
private static DisplayMode modes[] = {
new DisplayMode(640, 480, 32, 0),
new DisplayMode(1024, 768, 32, 0),
};
I then get a list of supported display Modes from the Video Card, comparing the list and use the first matching display mode.
/////////////////////////////////////////////
////Variable Declaration
/////////////////////////////////////////////
private GraphicsDevice vc;
/////////////////////////////////////////////
//Give Video Card Access to Monitor Screen
/////////////////////////////////////////////
public ScreenManager(){
GraphicsEnvironment e = GraphicsEnvironment.getLocalGraphicsEnvironment();
vc = e.getDefaultScreenDevice();
}
/////////////////////////////////////////////
//Find Compatible display mode
/////////////////////////////////////////////
//Compare Display mode supported by the application and display modes supported by the video card
//Use the first matching display mode;
public DisplayMode findFirstCompatibleMode(DisplayMode modes[]){
DisplayMode goodModes[] = vc.getDisplayModes();
for(int x=0; x<modes.length; x++){
for(int y=0; y<goodModes.length; y++){
if (displayModesMatch(modes[x], goodModes[y])){
return modes[x];
}
}
}
return null;
}
/////////////////////////////////////////////
//Checks if two Display Modes match each other
/////////////////////////////////////////////
public boolean displayModesMatch(DisplayMode m1, DisplayMode m2){
//Test Resolution
if (m1.getWidth() != m2.getWidth() || m1.getHeight() != m2.getHeight()){
return false;
}
//Test BitDepth
if (m1.getBitDepth() != DisplayMode.BIT_DEPTH_MULTI && m2.getBitDepth() != DisplayMode.BIT_DEPTH_MULTI
&& m1.getBitDepth() != m2.getBitDepth()){
return false;
}
//Test Refresh Rate
if (m1.getRefreshRate() != DisplayMode.REFRESH_RATE_UNKNOWN &&
m2.getRefreshRate() != DisplayMode.REFRESH_RATE_UNKNOWN &&
m1.getRefreshRate() != m2.getRefreshRate()){
return false;
}
return true;
}
Currently, I am only supporting two resolutions, 640x480 and 1024x768.
In order to have every element of my game available in both resolutions, first I find how much the screen is resized and store this value in a variable called resizeRatio
private void getResizeRatio(){
resizeRatio = (double)1024/(double)s.getWidth();
//s.getWidth() returns the current width of the screen.
}
And with every image I import, i would divide the image height and width by this resizeRatio.
/////////////////////////////////////////////
//Scale the image to the right proportion for the resolution
/////////////////////////////////////////////
protected Image scaleImage(Image in){
Image out = in.getScaledInstance((int)(in.getWidth(null)/resizeRatio), (int)(in.getHeight(null)/resizeRatio), Image.SCALE_SMOOTH);
return out;
}
This is all fine and good, until my application grew bigger and bigger. Soon I realize I forgot to resize some of the icons, and they are all at the wrong place when resolution is 640x480.
Additionally, I realize I must scale, not just the size of all my images, but all the movement speed, and all the positions as well, since having my character move at 5px per refresh makes him move significantly faster when displayed at 640x480 than when displayed at 1024x768
So my question is, instead of individually scaling every image, every icon, and every movement, is there a way to scale everything all at once? Or rather, there must be another way of doing this so could someone please tell me?
Thank you for reading and any help would be much appreciated.
In the paintComponent(Graphics g) or paint method you can do with Graphics2D.scale:
private double scale = 0.75;
#override
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D)g; // The newer more ellaborate child class.
g2.scale(scale, scale);
...
g2.scale(1/scale, 1/scale);
}
I'm working on the android half of a cross-platform android/ios framework that lets you write apps in JS that work on both platforms. I say this because it means I can't use things like 9-patches to get this effect. Full code at https://github.com/mschulkind/cordova-true-native-android
Here are two screenshots of the problem:
-Images redacted because I'm too new to be this useful. I will have to add them when I'm no longer a newbie.-
Here's the code that generates the drawable from https://github.com/mschulkind/cordova-true-native-android/blob/master/src/org/apache/cordova/plugins/truenative/ViewPlugin.java#L146
// Borrowed from:
// http://www.betaful.com/2012/01/programmatic-shapes-in-android/
private class ViewBackground extends ShapeDrawable {
private final Paint mFillPaint, mStrokePaint;
private final int mBorderWidth;
public ViewBackground(
Shape s, int backgroundColor, int borderColor, int borderWidth) {
super(s);
mFillPaint = new Paint(this.getPaint());
mFillPaint.setColor(backgroundColor);
mStrokePaint = new Paint(mFillPaint);
mStrokePaint.setStyle(Paint.Style.STROKE);
mStrokePaint.setStrokeWidth(borderWidth);
mStrokePaint.setColor(borderColor);
mBorderWidth = borderWidth;
}
#Override
protected void onDraw(Shape shape, Canvas canvas, Paint paint) {
shape.resize(canvas.getClipBounds().right, canvas.getClipBounds().bottom);
Matrix matrix = new Matrix();
matrix.setRectToRect(
new RectF(
0, 0,
canvas.getClipBounds().right, canvas.getClipBounds().bottom),
new RectF(
mBorderWidth/2, mBorderWidth/2,
canvas.getClipBounds().right - mBorderWidth/2,
canvas.getClipBounds().bottom - mBorderWidth/2),
Matrix.ScaleToFit.FILL);
canvas.concat(matrix);
shape.draw(canvas, mFillPaint);
if (mBorderWidth > 0) {
shape.draw(canvas, mStrokePaint);
}
}
}
This has happened both when the drawable was set as the background of the EditText directly and when I set it as the background of a parent view around the EditText.
Anyone have an idea of what's going on here or what avenues I should explore?
Looks like you want to draw a rounded rectangle.
To achieve such a style, it is simpler to use a XML drawable.
You simply put a XML file into the drawable/ directory. Here you can describe the desired shape.
Some documentation about XML drawables is here : http://idunnolol.com/android/drawables.html
Look at the tag.
I have some old Java 2D code I want to reuse, but was wondering, is this the best way to get the highest quality images?
public static BufferedImage getScaled(BufferedImage imgSrc, Dimension dim) {
// This code ensures that all the pixels in the image are loaded.
Image scaled = imgSrc.getScaledInstance(
dim.width, dim.height, Image.SCALE_SMOOTH);
// This code ensures that all the pixels in the image are loaded.
Image temp = new ImageIcon(scaled).getImage();
// Create the buffered image.
BufferedImage bufferedImage = new BufferedImage(temp.getWidth(null),
temp.getHeight(null), BufferedImage.TYPE_INT_RGB);
// Copy image to buffered image.
Graphics g = bufferedImage.createGraphics();
// Clear background and paint the image.
g.setColor(Color.white);
g.fillRect(0, 0, temp.getWidth(null),temp.getHeight(null));
g.drawImage(temp, 0, 0, null);
g.dispose();
// j2d's image scaling quality is rather poor, especially when
// scaling down an image to a much smaller size. We'll post filter
// our images using a trick found at
// http://blogs.cocoondev.org/mpo/archives/003584.html
// to increase the perceived quality....
float origArea = imgSrc.getWidth() * imgSrc.getHeight();
float newArea = dim.width * dim.height;
if (newArea <= (origArea / 2.)) {
bufferedImage = blurImg(bufferedImage);
}
return bufferedImage;
}
public static BufferedImage blurImg(BufferedImage src) {
// soften factor - increase to increase blur strength
float softenFactor = 0.010f;
// convolution kernel (blur)
float[] softenArray = {
0, softenFactor, 0,
softenFactor, 1-(softenFactor*4), softenFactor,
0, softenFactor, 0};
Kernel kernel = new Kernel(3, 3, softenArray);
ConvolveOp cOp = new ConvolveOp(kernel, ConvolveOp.EDGE_NO_OP, null);
return cOp.filter(src, null);
}
Chris Campbell has an excellent and detailed write-up on scaling images - see this article.
Chet Haase and Romain Guy also have a detailed and very informative write-up of image scaling in their book, Filthy Rich Clients.
Adding some clarifying information here.
No, that isn't the best way to get a good looking scaled image in Java. Use of getScaledInstance and the underlying AreaAveragingScaleFilter are deprecated by the Java2D team in favor of some more advanced methods.
If you are just trying to get a good-looking thumbnail, using Chris Campbell's method as suggested by David is the way to go. For what it's worth, I have implemented that algorithm along with 2 other faster methods in a Java image-scaling library called imgscalr (Apache 2 license). The point of the library was to specifically address this question in a highly tuned library that is easy to use:
BufferedImage thumbnail = Scalr.resize(srcImg, 150);
To get the best-looking scaled instance possible in Java, the method call would look something like this:
BufferedImage scaledImg = Scalr.resize(img, Method.QUALITY,
150, 100, Scalr.OP_ANTIALIAS);
The library will scale the original image using the incremental-scaling approach recommended by the Java2D team and then to make it look even nicer a very mild convolveop is applied to the image, effectively anti-aliasing it slightly. This is really nice for small thumbnails, not so important for huge images.
If you haven't worked with convolveops before, it's a LOT of work just to get the perfect looking kernel for the op to look good in all use-cases. The OP constant defined on the Scalr class is the result of a week of collaboration with a social networking site in Brazil that had rolled out imgscalr to process profile pictures for it's members. We went back and forth and tried something like 10 different kernels until we found one that was subtle enough not to make the image look soft or fuzzy but still smooth out the transitions between pixel values so the image didn't look "sharp" and noisey at small sizes.
If you want the best looking scaled image regardless of speed, go with Juha's suggestion of using the java-image-scaling library. It is a very comprehensive collection of Java2D Ops and includes support for the Lanczsos algorithm which will give you the best-looking result.
I would stay away from JAI, not because it's bad, but because it is just a different/broader tool than what you are trying to solve. Any of the previous 3 approaches mentioned will give you great looking thumbnails without needing to add a whole new imaging platform to your project in fewer lines of code.
You can use JAI (Java Advanced Imaging) to get more sophisticated image resizing options. See https://jai.dev.java.net/. These allow you much more flexibility than the java.awt.image package.
You could also look into java-image-scaling library.
You can Resize Image using a Open Source Library
enter link description here
I have done with Large Image to Small and result excellent, keeping the aspect ratio fine.
import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.ImageIO;
import javax.swing.JOptionPane;
import org.imgscalr.*;
import org.imgscalr.Scalr.Method;
public class ImageScaller {
static String SRC_FILES_PATH = "I:\\BigGreen\\";
static String IMAGE_FILE_PATH = "I:\\Resized\\";
public ImageScaller() {
}
public static void main(String[] args) {
// TODO Auto-generated method stub
try
{
ResizeLoad(SRC_FILES_PATH);
}
catch(Exception ex)
{
System.out.println(ex.toString());
}
}
public static int ResizeLoad(String path)
{
String file;
File folder ;
File[] listOfFiles = null;
listOfFiles = null;
try
{
folder = new File(path);
listOfFiles = folder.listFiles();
for (int i = 0; i < listOfFiles.length; i++)
{
if (listOfFiles[i].isFile())
{
file = listOfFiles[i].getName();
ScalledImageWrite(listOfFiles[i].getPath(),file);
//System.out.println(file);
}
}
System.out.println("All Resized");
}
catch (Exception e)
{
JOptionPane.showMessageDialog(null,e.toString(),"Resize & Load :Exception",JOptionPane.WARNING_MESSAGE);
}
return listOfFiles.length;
}
private static File ScalledImageWrite(String path,String fileName)
{
try
{
BufferedImage img = ImageIO.read(new File(path));
BufferedImage scaledImg = Scalr.resize(img, Method.AUTOMATIC, 24, 24);
File destFile = new File(IMAGE_FILE_PATH + fileName);
ImageIO.write(scaledImg, "png", destFile);
//System.out.println("Done resizing");
return destFile;
}
catch (Exception e)
{
JOptionPane.showMessageDialog(null,e.toString(),"Scalled Images Write: Exception",JOptionPane.WARNING_MESSAGE);
return null;
}
}
}
Here is the output in pictorial format of this code.