I have a Swing app that can display an image by loading it into a JLabel. Up to Java 8, this worked fine. Since Java 9 there are changes intended to make apps resize on higher pixel densities, including rescaling images: when pixel density is high, images are scaled up. The results are ugly in many cases. As an example, if I have a photo with a high pixel count, my app scales it down to fit the available space, but then it's automatically scaled back up to allow for the higher pixel density - ugly.
A couple of options I can see for avoiding this problem are:
Implementing the interface java.awt.image.MultiResolutionImage which looks like it should help, but I don't see how to use that with JLabel.
Maybe there's a way to fool Swing into thinking that no pixel scaling is necessary, but I can't find anything like that
The following code is a simple example of displaying an image in a label. In Java 8, the image is displayed pixel-for-pixel. In Java 9+, the whole UI including the image is scaled up according to what your OS thinks is your pixel density. You should be able to set this scaling explicitly - Windows 10 has it under "Display", "Scale and Layout". Mine is set to 125% which seems to be a perfect amount to make almost any image look ugly.
import java.awt.*;
import javax.imageio.*;
import javax.swing.*;
import java.io.*;
public class Img extends JFrame {
public static void main( String[] args ) throws IOException {
new Img( new File( args[0] ) );
}
private Img( File file ) throws IOException {
super( "Img: " + file.getName () );
ImageIcon img = new ImageIcon( ImageIO.read( file ) );
JLabel label = new JLabel( img );
label.setPreferredSize( new Dimension( img.getIconWidth(), img.getIconHeight() ) );
add( label );
setDefaultCloseOperation( EXIT_ON_CLOSE );
pack();
setVisible( true );
}
}
Related
I know they are quite a few forums that kinda talks about this, but my problem is that I have a JLabel with an icon which I do NOT want to scale when resolution is <> 100% ! I.e. (sorry, in french):
Initially, I set my JLabel icon with something like:
lblImage.setIcon( getNoteIcon() );
//...
ImageIcon getNoteIcon(){
return new ImageIcon( getClass().getResource('path/to/file.png') );
}
This results are that my image displays blurry when HiDPI resolution is set to higher than 100%. Examples:
At 100%:
And at 150%:
You can see that the music note image is fuzzier in the 150% resolution.
What I want: I DO NOT want it to up or downscale the image! I want to keep the original size, unless I can provide different image sizes for various resolutions.
That being said, I looked and tried to use the BaseMultiResolutionImage class to deal with resolutions, passing along an array of the "same size image", but I get the same results when I change the resolution. This is the code I tried :
ImageIcon getNoteIcon(){
List<Image> imgList = new ArrayList<Image>();
/* all images are width = 108 height = 83 */
imgList.add(ImageIO.read(new File('path/to/file.png'))); // 100%
imgList.add(ImageIO.read(new File('path/to/file.png'))); // 125%
imgList.add(ImageIO.read(new File('path/to/file.png'))); // 150%
imgList.add(ImageIO.read(new File('path/to/file.png'))); // 175%
imgList.add(ImageIO.read(new File('path/to/file.png'))); // 200%
BaseMultiResolutionImage multiResolutionImage = new BaseMultiResolutionImage(imgList.toArray(new Image[0]));
return new ImageIcon(
multiResolutionImage
);
}
I don't understand what I'm doing wrong, or if I'm even using BaseMultiResolutionImage correctly. Finally, if I do manage say to give 3 different images for 3 resolutions, (i.e. 100, 125, 150%), is there a way to tell it the "default" one to use when I do not have an image for a given resolution (i.e. 200%) ?
Can anyone shed a light on all of this? Seems like a lot of confusion on my end just to get an image to NOT scale in my window...
Much thanks!
Pat
UPDATE AFTER "PROPER" USE OF BaseMultiResolutionImage:
try {
List<Image> imgList = new ArrayList<Image>();
imgList.add(ImageIO.read(getClass().getResource("images/notes/hidpi_100/file.png"))); // 100% - 108x83
imgList.add(ImageIO.read(getClass().getResource("images/notes/hidpi_125/file.png"))); // 125% - 135x104
imgList.add(ImageIO.read(getClass().getResource("images/notes/hidpi_150/file.png"))); // 150% - 162x124
imgList.add(ImageIO.read(getClass().getResource("images/notes/hidpi_175/file.png"))); // 175% - 189x145
imgList.add(ImageIO.read(getClass().getResource("images/notes/hidpi_200/file.png"))); // 200% - 216x166
BaseMultiResolutionImage multiResolutionImage = new BaseMultiResolutionImage(imgList.toArray(new Image[0]));
ImageIcon icon = new ImageIcon(multiResolutionImage);
return icon;
}
catch(IOException ex){
ex.printStackTrace();
return null;
}
On screen font DPI set to 175% in Windows, this results in:
Before using BaseMultiResolutionImage
After using BaseMultiResolutionImage
Here is a comparison of what I get (left), with the actual image file it's supposed to show on the right hand side (mind you Paint doesn't take into account background transparency):
Any ideas as to why this "choppy" image is showing? What am I missing ?
Thanks again.
Pat
So I took it upon myself to learn Java.
A decision I regret more with every crash of Elipse.
Fortunately I have actually managed to get this block of my self inflicted project to actually 'work' but obviously being self taught I am quite sure I have made a lot of errors in my layout and
In total, the program will create a JFrame then stick a JscrollPane inside that into which it inserts a a JPanel (wrapPage). It then loops through a process that generates an array of TMTile Objects which are extended JPanels containing the tile images which are drawn from a source folder of jpg images. Once that has finished it places that array as a grid using the GridBagLayout within the wrapPage Jpanel resulting in a nice little perfect maze.
That all works perfectly, but the big let down is that the size of the image used to create the tiles is dictating everything. I can't for the life of me figure out how to scale the image and efforts to find a suitable process have only got me methods of creating new image files or alternating between stretching and tiling images to fit a within their containing component or suggestions I just couldn't follow to save my life.
Fortunately. The image handling is part of the TMTile class file! This means I can show you the entire relevant bit of script;
The following are imported for use in this file
from java.awt: Color, GridBagConstraints, GridBagLayout, Insets
from javax.swing: ImageIcon, JLabel, JPanel
public class TMTile extends JPanel
{
private static final long serialVersionUID = 1L;
private int paths; // values 0 to 15, uses bitwise & | to set or check open paths
private JLabel tileWrap; // to contain the ImageIcon
private ImageIcon tileImg; // the image to be used
GridBagConstraints bag;
public TMTile( int inDir ) // called by maze constructor
{
paths = inDir;
this.setBackground( Color.RED ); // so I can tell if the image didn't load right
this.setLayout( new GridBagLayout() ); // GridBagLayout is probably overkill but it what I am most familiar with now.
bag = new GridBagConstraints();
bag.insets = new Insets( 0, 0, 0, 0 );
tileImg = tileImage( paths );
tileWrap = new JLabel( "", tileImg, JLabel.CENTER );
this.add( tileWrap, bag );
}
public void open( int inDir ) // called by maze constructor when tile value changes resulting from the perfect maze backtrack method
{
paths = paths | inDir;
tileImg = tileImage( paths );
tileWrap.setIcon( tileImg );
}
private ImageIcon tileImage( int paths ) // created to cut down on duplicate code and make updating easier
{
String inEnd;
if(paths < 10)
{
inEnd = "0"+ paths;
}
else
{
inEnd = ""+ paths;
}
ImageIcon tileImg = new ImageIcon( "imgs/MAZE_"+ inEnd +".jpg" );
System.out.println( "imgs/MAZE_"+ inEnd +".jpg" );
Image newimg = tileImg.getImage().getScaledInstance( 40, 40, java.awt.Image.SCALE_DEFAULT );
tileImg = new ImageIcon( newimg );
return tileImg;
}
public int getOpen()
{
return paths;
}
}
Thanks to nachokk and MadProgrammer I now once again have a working maze program and the maze tiles are scalable. That just leaves the final goal of doing away with individual tile .jpgs and switching to a single image file with all 16 stored within in.
What I would love to have is the ability to utilize a single large image file which is divided into 16 sections, 1 section for each tile value. I started out working toward this goal but had to abandon it fairly quickly as I couldn’t figure out how to only display the section of the image needed which would also need to be scaled in the way described above.
Since I am very much still learning Java advice on any alternatives is welcome but ideally I would love to know how to accomplish this as planned.
I have a Graphics2D object and I want to set up the background of the object. It has a setBackground method, which has a Color parameter. This way I can set the color of the background.
My question is: how can I set the transparency of the background of the object? Can I somehow tell it to be completely transparent? Can I somehow tell it to be completely opaque? Can I somehow tell it to have 0.8 transparency/opacity? How can I set these values?
I have seen that there are int predefined values called TRANSLUCENT and OPAQUE, but I am not sure how can I use them.
Maybe the correct usage is to call the constructor of Color with an int parameter?
You can construct a Color object by specifying a transparency. For example the following code constructs a RED color with 50% transparency
Color c=new Color(1f,0f,0f,.5f );
You can call the constructor of Color in the following way:
Color c = new Color(r,g,b,a);
where a is the alpha (transparency) value.
As with all Java classes, you can find this information in the official API: http://docs.oracle.com/javase/7/docs/api/java/awt/Color.html
It's a really good resource and can spare you waiting for an answer on here.
You may try this if you are using a JPanel :
jPanel1.setOpaque(false);
jPanel1.setBackground(new Color(0,0,0,200));
/*This will put a transparent black color on a panel, the important part of the code is: .setBackground(new Color(0,0,0,200));*/
Java is actually pretty good at this stuff, you can achieve transparency and much more. Here's some code for a simple transparent window I copied from oracle:
package misc;
import java.awt.*;
import javax.swing.*;
import static java.awt.GraphicsDevice.WindowTranslucency.*;
public class TranslucentWindowDemo extends JFrame {
public TranslucentWindowDemo() {
super("TranslucentWindow");
setLayout(new GridBagLayout());
setSize(300,200);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Add a sample button.
add(new JButton("I am a Button"));
}
public static void main(String[] args) {
// Determine if the GraphicsDevice supports translucency.
GraphicsEnvironment ge =
GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice gd = ge.getDefaultScreenDevice();
//If translucent windows aren't supported, exit.
if (!gd.isWindowTranslucencySupported(TRANSLUCENT)) {
System.err.println(
"Translucency is not supported");
System.exit(0);
}
JFrame.setDefaultLookAndFeelDecorated(true);
// Create the GUI on the event-dispatching thread
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
TranslucentWindowDemo tw = new TranslucentWindowDemo();
// Set the window to 55% opaque (45% translucent).
tw.setOpacity(0.55f);
// Display the window.
tw.setVisible(true);
}
});
}
}
Look here for more information.
If what you want is to give a transparent effect use the Color properties to 4 variables:
this.setBackground (new Color (0,0,0, .5f));
this gives the background the RGB color of the first three parameters (*new Color (** 0,0,0, **. 5f)*) and the fourth is used to determine the percentage of opacity (opaque )
If you want the background not to be displayed, use the value null
this.setBackground (null);
Many use setOpaque (false); but that takes away the padding not the background.
Use the constructor of the color like this:
Color color = new Color(152,251,152, 50);
The value 50 is for the transparency.
I Have 1 single java app. ONly Containing 1 JMenu with 48 JMenuItem(s).
Each JMenuItems i set the Icon with Animated GIF.
I have no problem creating it.
And I also have no problem when Running it.
But the problem came, when I running it, and then viewing the JMenuItem.
My GIF(s) Animation seems consuming the PC Process very high!
And the ANimation seems jumping too fast! It's not the usual animation framerate, tough.
So anytime when I run the java app, and then trying the JMenuItem, The CPU Process
is getting higher...!
Look at this Preview: It's taken when My java app runs.
What's the best way to solve this?
What result do you get from this SSCCE? My system peaks to 40 at start-up then jumps to an average of around 20-30. The rendering of the animated GIFs is somewhat jerky.
import java.awt.*;
import javax.swing.*;
class Gif48 {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
String url = "http://1point1c.org/gif/thum/plnttm.gif";
int w = 12;
int h = 4;
JPanel p = new JPanel(new GridLayout(h,w,2,2));
String pre = "<html><body><img src='";
String post = "'>";
for (int ii=0; ii<w*h; ii++) {
JLabel l = new JLabel(
pre + url + post );
l.setBackground(Color.BLACK);
l.setOpaque(true);
p.add(l);
}
JOptionPane.showMessageDialog(null, p);
}
});
}
}
What's the best way to solve this?
Don't use animated GIFs in the controls of GUIs.
I have found the solutions guys! The GIF Animation has lots of frames. In each frames they have their own timing. When I reopened one single GIF Animation File, I could see clearly that the 0-time framerate is the problem. Now I should Changed it to somewhat above 0.1 framerate, that's all problem solved.
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 9 years ago.
Improve this question
I am trying to achieve better performance for my Java SWT application, and I just found out it is possible to use OpenGL in SWT. It seems there are more than one Java binding for OpenGL. Which one do you prefer?
Note that I have never used OpenGL before, and that the application needs to work on Windows, Linux and Mac OS X.
I'd suggest checking out LWJGL, the LightWeight Java Game Library. It's got OpenGL bindings, but it also has OpenAL bindings and some great tutorials to get you started.
Just keep in mind that Swing/SWT and OpenGL are generally used for entirely different things. You may end up wanting to use a combination of both. Just try LWJGL out and see how well it fits with what you're doing.
JOGL
My reasons can be quoted off the previously linked site:
JOGL provides full access to the APIs in the OpenGL 2.0 specification as well as nearly all vendor extensions, and integrates with the AWT and Swing widget sets.
Also if you want to have some fun learning and poking around, Processing is an excellent way to start (Processing also uses JOGL btw...)
JOGL is probably the only option worth considering.
Notice that there are at least two options for integrating it into an SWT application. There's a GLCanvas that belongs to SWT and a GLCanvas that belongs to AWT.
The one in SWT is not feature complete and is not really maintained. It's much better to use the AWT GLCanvas inside a SWT_AWT container.
Some code from a recent project:
import org.eclipse.swt.*;
import org.eclipse.swt.layout.*;
import org.eclipse.swt.widgets.*;
import javax.media.opengl.*;
import javax.media.opengl.glu.*;
import org.eclipse.swt.awt.SWT_AWT;
import org.eclipse.swt.events.*;
public class Main implements GLEventListener
{
public static void main(String[] args)
{
Display display = new Display();
Main main = new Main();
main.runMain(display);
display.dispose();
}
void runMain(Display display)
{
final Shell shell = new Shell(display);
shell.setText("Q*bert 3D - OpenGL Exercise");
GridLayout gridLayout = new GridLayout();
gridLayout.marginHeight = 0;
gridLayout.marginWidth = 0;
shell.setLayout(gridLayout);
// this allows us to set particular properties for the GLCanvas
GLCapabilities glCapabilities = new GLCapabilities();
glCapabilities.setDoubleBuffered(true);
glCapabilities.setHardwareAccelerated(true);
// instantiate the canvas
final GLCanvas canvas = new GLCanvas(glCapabilities);
// we can't use the default Composite because using the AWT bridge
// requires that it have the property of SWT.EMBEDDED
Composite composite = new Composite(shell, SWT.EMBEDDED);
GridData ld = new GridData(GridData.FILL_BOTH);
composite.setLayoutData(ld);
// set the internal layout so our canvas fills the whole control
FillLayout clayout = new FillLayout();
composite.setLayout(clayout);
// create the special frame bridge to AWT
java.awt.Frame glFrame = SWT_AWT.new_Frame(composite);
// we need the listener so we get the GL events
canvas.addGLEventListener(this);
// finally, add our canvas as a child of the frame
glFrame.add(canvas);
// show it all
shell.open();
// the event loop.
while (!shell.isDisposed ()) {
if (!display.readAndDispatch ()) display.sleep ();
}
}
JOGL will give you best performance and portability. But be aware that learning JOGL, which is essentially the same as learning OpenGL, is not easy.
Personally, I'm not even aware of Java bindings for OpenGL other than JOGL -- I think JOGL is pretty much the standard for Java OpenGL.
It works in Windows, Linux, and OS X, but you might want to read over the official documentation for some notes about specific issues in each platform.
Keep in mind that the OpenGL paradigm is quite different from Swing/AWT or the Java 2D API; OpenGL is not a drop-in replacement for Swing.
We've had lots of luck at work using JOGL. The new 2.0 version is at http://jogamp.org/ (the last "old" version is at http://download.java.net/media/jogl/builds/archive/jsr-231-1.1.1a/).
For JOGL 2 with SWT specifically, I've got a series of tutorials starting at http://wadeawalker.wordpress.com/2010/10/09/tutorial-a-cross-platform-workbench-program-using-java-opengl-and-eclipse/ that demonstrates exactly how to make cross-platform JOGL SWT applications, complete with installable native binaries.
Or if you don't want to use Eclipse RCP, here's an even simpler example that just draws one triangle with JOGL 2 and SWT. To build it, put it in a project with swt.jar (from http://www.eclipse.org/swt/) and the latest JOGL autobuild .jar and .dll files (from http://jogamp.org/). The only problem with this simple example is that it won't be cross-platform without some extra help -- you need the ability that Eclipse RCP gives you to bundle multiple sets of platform libraries together into one project.
package name.wadewalker.onetriangle;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.opengl.GLCanvas;
import org.eclipse.swt.opengl.GLData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
import javax.media.opengl.GL;
import javax.media.opengl.GLProfile;
import javax.media.opengl.GL2;
import javax.media.opengl.GLContext;
import javax.media.opengl.GLDrawableFactory;
import javax.media.opengl.glu.GLU;
public class OneTriangle {
public static void main(String [] args) {
GLProfile.initSingleton( true );
GLProfile glprofile = GLProfile.get( GLProfile.GL2 );
Display display = new Display();
Shell shell = new Shell( display );
shell.setLayout( new FillLayout() );
Composite composite = new Composite( shell, SWT.NONE );
composite.setLayout( new FillLayout() );
GLData gldata = new GLData();
gldata.doubleBuffer = true;
// need SWT.NO_BACKGROUND to prevent SWT from clearing the window
// at the wrong times (we use glClear for this instead)
final GLCanvas glcanvas = new GLCanvas( composite, SWT.NO_BACKGROUND, gldata );
glcanvas.setCurrent();
final GLContext glcontext = GLDrawableFactory.getFactory( glprofile ).createExternalGLContext();
// fix the viewport when the user resizes the window
glcanvas.addListener( SWT.Resize, new Listener() {
public void handleEvent(Event event) {
setup( glcanvas, glcontext );
}
});
// draw the triangle when the OS tells us that any part of the window needs drawing
glcanvas.addPaintListener( new PaintListener() {
public void paintControl( PaintEvent paintevent ) {
render( glcanvas, glcontext );
}
});
shell.setText( "OneTriangle" );
shell.setSize( 640, 480 );
shell.open();
while( !shell.isDisposed() ) {
if( !display.readAndDispatch() )
display.sleep();
}
glcanvas.dispose();
display.dispose();
}
private static void setup( GLCanvas glcanvas, GLContext glcontext ) {
Rectangle rectangle = glcanvas.getClientArea();
glcanvas.setCurrent();
glcontext.makeCurrent();
GL2 gl = glcontext.getGL().getGL2();
gl.glMatrixMode( GL2.GL_PROJECTION );
gl.glLoadIdentity();
// coordinate system origin at lower left with width and height same as the window
GLU glu = new GLU();
glu.gluOrtho2D( 0.0f, rectangle.width, 0.0f, rectangle.height );
gl.glMatrixMode( GL2.GL_MODELVIEW );
gl.glLoadIdentity();
gl.glViewport( 0, 0, rectangle.width, rectangle.height );
glcontext.release();
}
private static void render( GLCanvas glcanvas, GLContext glcontext ) {
Rectangle rectangle = glcanvas.getClientArea();
glcanvas.setCurrent();
glcontext.makeCurrent();
GL2 gl = glcontext.getGL().getGL2();
gl.glClear( GL.GL_COLOR_BUFFER_BIT );
// draw a triangle filling the window
gl.glLoadIdentity();
gl.glBegin( GL.GL_TRIANGLES );
gl.glColor3f( 1, 0, 0 );
gl.glVertex2f( 0, 0 );
gl.glColor3f( 0, 1, 0 );
gl.glVertex2f( rectangle.width, 0 );
gl.glColor3f( 0, 0, 1 );
gl.glVertex2f( rectangle.width / 2, rectangle.height );
gl.glEnd();
glcanvas.swapBuffers();
glcontext.release();
}
}