Generate thumbnail of website? - java

For my application I need to dynamically create thumbnails of websites. So far I have this code from SO:
public class CreateWebsiteThumbnail {
private static final int WIDTH = 128;
private static final int HEIGHT = 128;
private BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
public void capture(Component component) {
component.setSize(image.getWidth(), image.getHeight());
Graphics2D g = image.createGraphics();
try {
component.paint(g);
} finally {
g.dispose();
}
}
private BufferedImage getScaledImage(int width, int height) {
BufferedImage buffer = new BufferedImage(width, height,
BufferedImage.TYPE_INT_RGB);
Graphics2D g = buffer.createGraphics();
try {
g.drawImage(image, 0, 0, width, height, null);
} finally {
g.dispose();
}
return buffer;
}
public void save(File png, int width, int height) throws IOException {
ImageIO.write(getScaledImage(width, height), "png", png);
}
public static void main(String[] args) throws IOException {
Shell shell = new Shell();
Browser browser = new Browser(shell, SWT.EMBEDDED);
browser.setUrl("http://www.google.com");
CreateWebsiteThumbnail cap = new CreateWebsiteThumbnail();
cap.capture(What her?);
cap.save(new File("foo.png"), 64, 64);
}
}
But as you can see here, I don't know which part of the browser I should pass to my capture method. Any hints?

I don't see, how the code you provided could work. The Shell is not opened, it has no size, the Browser didn't get time to actually load anything, no event loop seems to be running to enable any drawing, ...
The following code does a screenshot of a page using SWT browser:
import java.io.IOException;
import org.eclipse.swt.SWT;
import org.eclipse.swt.browser.Browser;
import org.eclipse.swt.browser.ProgressEvent;
import org.eclipse.swt.browser.ProgressListener;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.ImageLoader;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
public class CreateWebsiteThumbnail {
private static final int WIDTH = 800;
private static final int HEIGHT = 600;
public static void main( String[] args ) throws IOException {
final Display display = new Display();
final Shell shell = new Shell();
shell.setLayout(new FillLayout());
final Browser browser = new Browser(shell, SWT.EMBEDDED);
browser.addProgressListener(new ProgressListener() {
#Override
public void changed( ProgressEvent event ) {}
#Override
public void completed( ProgressEvent event ) {
shell.forceActive();
display.asyncExec(new Runnable() {
#Override
public void run() {
grab(display, shell, browser);
}
});
}
});
browser.setUrl("http://www.google.com");
shell.setSize(WIDTH, HEIGHT);
shell.open();
while ( !shell.isDisposed() ) {
if ( !display.readAndDispatch() ) display.sleep();
}
display.dispose();
}
private static void grab( final Display display, final Shell shell, final Browser browser ) {
final Image image = new Image(display, browser.getBounds());
GC gc = new GC(browser);
gc.copyArea(image, 0, 0);
gc.dispose();
ImageLoader loader = new ImageLoader();
loader.data = new ImageData[] { image.getImageData() };
loader.save("foo.png", SWT.IMAGE_PNG);
image.dispose();
shell.dispose();
}
}
But there are some serious caveats:
You cannot do this off-screen. SWT screenshots are just a copy of the current Display.
The window containing your browser must be on top, when taking the screenshot.
The page should be visible after onLoad (which is actually not the case with google.com, but works for me because of the asyncExec call anyway - if you get a white image, try another URL)
The result is dependant on your OS and its installed browsers
I'd go with a non Java-solution, in order to get off screen drawing. I believe the linked question might help you to get further.

As far as I know, when you use a Browser object, the webpage you load is rendered directly on the Composite object you pass to it through the constructor. In your case, it is rendered on your Shell item which is a window-style object. There is no method to render the webpage directly on, say, an Image object.
You can try, though, to instantiate your Browser on a Canvas object and save the image directly from there.
Unfortunately I am unable to test whether this works or not because I have neither Eclipse nor SWT installed; I am pretty sure though that, if what you want to do is doable, this is the only way.

I did some experimenting myself. My intuition was to try on JEditorPane just like mentioned in the answer your code is based on. I do not know of how much help it is going to be but it might help. I gives some results, but from obvious reasons, luck of css support etc in JEditorPane it all looks ugly.
This is my code:
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.text.html.HTMLEditorKit;
public class WebPageToImageThumbnail {
public static void main(String[] a) throws Exception {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JEditorPane editorPane = new JEditorPane();
editorPane.setEditorKit(new HTMLEditorKit());
try {
editorPane.setPage(new URL("http://weblogs.java.net/blog/alex2d/archive/2008/12/jwebpane_projec.html"));
} catch (IOException ex) {
}
final JPanel panel = new JPanel(new BorderLayout());
panel.add(new JScrollPane(editorPane), BorderLayout.CENTER);
panel.add(new JButton(new AbstractAction("SAVE") {
#Override
public void actionPerformed(ActionEvent e) {
BufferedImage image = capture(editorPane);
try {
save(image, new File("foo.png"), 64, 64);
} catch (IOException ex) {
}
}
}), BorderLayout.SOUTH);
frame.setContentPane(panel);
frame.setSize(600, 400);
frame.setVisible(true);
}
});
}
public static BufferedImage capture(Component component) {
BufferedImage image = new BufferedImage(component.getWidth(), component.getHeight(), BufferedImage.TYPE_INT_RGB);//component.setSize(image.getWidth(), image.getHeight());
Graphics2D g = image.createGraphics();
try {
component.paint(g);
} finally {
g.dispose();
}
return image;
}
private static BufferedImage getScaledImage(BufferedImage image, int width, int height) {
BufferedImage buffer = new BufferedImage(width, height,
BufferedImage.TYPE_INT_RGB);
Graphics2D g = buffer.createGraphics();
try {
g.drawImage(image, 0, 0, width, height, null);
} finally {
g.dispose();
}
return buffer;
}
public static void save(BufferedImage image, File png, int width, int height) throws IOException {
ImageIO.write(getScaledImage(image, width, height), "png", png);
}
}
I also did some digging about the SWT I found some thinks that might be of use, but since I currently luck time, I cannot test. From what I read I have to agree with #Emanuele Bezzi (+1) that we are going to have to use the Shell somehow to get the content of the site, in which we are effectively only interested.
I found out that Shell has print method which takes GC object which can paint including to Image and other interesting to us stuff, the documentation says: "Class GC is where all of the drawing capabilities that are supported by SWT are located. Instances are used to draw on either an Image, a Control, or directly on a Display.".
Yet at this particular moment it is not clear to me how to exactly get it to do what I want. Anyway I am raising this point just to make you aware. I still need to look into it further.

Maybe you're better off rendering the page offscreen right from the start. For such a task you may for example use "flying saucer" (which is xhtml only, so you need to tidy up). There seem to be also some tools to interface Webkit directly from Java.

Related

Drawing an image on top of an image in a JComponent erases part of the bottom image

I am making a 2d game and I need to draw an image on top of another. After I draw the first image(the larger one, jpg), the second image(the smaller one,png) erases from where the second image is to the lower right hand corner. Like this:
I have looked into this a bit, and it was suggested that I use buffered images, so I did that with both images and the problem remains. Here is one post I looked at: How to draw an image over another image?. I have also seen some people suggesting graphics2d, though I did not really understand the reason to use them or how to use them. I am new to java graphics and images, so it is probably a silly mistake.
Here is my code. Thank you.
import java.awt.*;
import java.awt.event.*;
import java.net.*;
import javax.swing.*;
import java.util.ArrayList;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import java.io.IOException;
public class DisplayExample extends JComponent
{
private BufferedImage backgroundImage;
private String backgroundName;
private BufferedImage image; //image to draw
private int imageX; //position of left edge of image
private int imageY; //position of top edge of image
private JFrame frame;
public static void main(String[] args)
{
DisplayExample example = new DisplayExample();
example.run();
}
public DisplayExample()
{
imageX = 200;
imageY = 200;
backgroundName = "backgroundShip.jpg";
URL backgroundURL = getClass().getResource(backgroundName);
if (backgroundURL == null)
throw new RuntimeException("Unable to load: " + backgroundName);
try{backgroundImage = ImageIO.read(backgroundURL);}catch(IOException ioe){}
//load image
String fileName = "explosion.png";
URL url = getClass().getResource(fileName);
if (url == null)
throw new RuntimeException("Unable to load: " + fileName);
//image = new ImageIcon(url).getImage();
try{image = ImageIO.read(url);}catch(IOException ioe){}
System.out.println(image instanceof BufferedImage);
setPreferredSize(new Dimension(1040,500)); //set size of drawing region
//need for keyboard input
setFocusable(true); //indicates that WorldDisp can process key presses
frame = new JFrame();
frame.getContentPane().add(this);
frame.pack();
frame.setVisible(true);
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
if(backgroundImage != null)
g.drawImage(backgroundImage,0,0,getWidth(), getHeight(), null);
g.drawImage(image, imageX, imageY, this);
}
public void run()
{
while(true)
{
imageY+=1;
repaint();
try{Thread.sleep(100);}catch(Exception e){}
}
}
}
So I took your code, added my own images and it runs fine for me.
Having said that, there are some areas you can improve:
You're running the risk of either blocking the Event Dispatching Thread or introducing a thread race condition into your code with your run method. You should consider using a Swing Timer instead. See How to use Swing Timers for more details. This allows you to schedule regular callbacks which are called within the context of the EDT, making it safer to update the context of the UI
You should only ever create or modify the state of the UI from within the context of the EDT, Swing is not thread safe. See Initial Threads for more details. Swing has been known to have "issues" when the UI is not initialised within the EDT
Scaling an image is expensive, you should avoid doing so from within the paint methods, instead, scale the image and keep a reference to the result and use it when you need to paint it.
You should consider using the key bindings API over KeyListener, it will solve many of the issues associated with using KeyListener. See How to Use Key Bindings for more details.
For example...
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class DisplayExample extends JComponent {
private BufferedImage backgroundImage;
private String backgroundName;
private BufferedImage image; //image to draw
private int imageX; //position of left edge of image
private int imageY; //position of top edge of image
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
DisplayExample example = new DisplayExample();
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(example);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public DisplayExample() {
imageX = 200;
imageY = 200;
try {
backgroundImage = ImageIO.read(new File("..."));
} catch (IOException ioe) {
ioe.printStackTrace();
}
//load image
try {
image = ImageIO.read(new File("..."));
} catch (IOException ioe) {
ioe.printStackTrace();
}
//need for keyboard input
//setFocusable(true); //indicates that WorldDisp can process key presses
// Use the key bindings API instead, causes less issues
Timer timer = new Timer(100, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
imageY += 1;
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return backgroundImage == null ? new Dimension(200, 200) : new Dimension(backgroundImage.getWidth(), backgroundImage.getHeight());
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
if (backgroundImage != null) {
// Scaling is expensive, don't do it here
int x = (getWidth() - backgroundImage.getWidth()) / 2;
int y = (getHeight() - backgroundImage.getHeight()) / 2;
g2d.drawImage(backgroundImage, x, y, this);
}
g2d.drawImage(image, imageX, imageY, this);
g2d.dispose();
}
}

Add white rectangle above a screenshot in a Graphics object (java)

I've got a method that's supposed to add some text to screenshots. The screenshot is fed into this method as a File object like this:
private void modifyScreenshot(File file) throws Exception {
String textToAdd = "Something something";
BufferedImage image = ImageIO.read(file);
Graphics g = image.getGraphics();
At this point, adding the text via g.drawString is easy to do. However, I want the text to not cover any of the actual screenshot, but be in a white area "above" the screenshot.
What I mean is, at this point, this is what the Graphics object looks like when it gets saved to file:
However, I want it to look like this instead, with the "Some text some text" being the string I specify in the code.
So, how would I be able to add white rectangle above the image where the text can be written?
EDIT: Note, this is not simply adding a string to an image. This involves "enlarging" the canvas to have white space for the string, so that the string is not over the actual image.
Here's the rough idea:
BufferedImage image = ImageIO.read(file);
int whiteSpaceHeight = 20;
BufferedImage result = new BufferedImage(image.getWidth(),
image.getHeight()+whiteSpaceHeight, image.getType());
Graphics graphics = result.getGraphics();
graphics.drawImage(image, 0, whiteSpaceHeight, null);
graphics.drawString(textToAdd, 0, whiteSpaceHeight/2);
(Edit: Answer was rewritten - see history for details)
The task that is indicated in the example image may in fact be a bit tricky: Namely, having multi-line text. But one simple solution here is to use a JLabel and a CellRendererPane for rendering the text, because it also supports HTML. So for a title like
String title =
"<html><font size=4>This <font color=#FF0000><b>Text</b></font><br>" +
"with line breaks<br>" +
"will be the title</font></html>");
with line breaks and colors and a dedicated font size, one can obtain the appropriate image:
Here is an example showing how this may be achieved:
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.imageio.ImageIO;
import javax.swing.CellRendererPane;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
public class TitleAdder
{
public static void main(String[] args)
{
addTitle("yS2aQ.png", "output.png",
"<html><font size=4>This <font color=#FF0000><b>Text</b></font><br>" +
"with line breaks<br>" +
"will be the title</font></html>");
}
private static void addTitle(
String inputFileName, String outputFileName, String title)
{
try (InputStream in = new FileInputStream(inputFileName);
OutputStream out = new FileOutputStream(outputFileName))
{
BufferedImage sourceImage = ImageIO.read(in);
BufferedImage targetImage =
addTitle(sourceImage, title);
ImageIO.write(targetImage, "png", out);
// Show the image, for testing
show(targetImage);
}
catch (IOException e)
{
e.printStackTrace();
}
}
private static BufferedImage addTitle(
BufferedImage sourceImage, String title)
{
JLabel label = new JLabel(title);
label.setBackground(Color.WHITE);
label.setForeground(Color.BLACK);
label.setOpaque(true);
int titleHeight = label.getPreferredSize().height;
int height = sourceImage.getHeight() + titleHeight;
BufferedImage targetImage = new BufferedImage(
sourceImage.getWidth(), height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = targetImage.createGraphics();
SwingUtilities.paintComponent(g, label, new CellRendererPane(),
0, 0, sourceImage.getWidth(), titleHeight);
g.drawImage(sourceImage, 0, titleHeight, null);
g.dispose();
return targetImage;
}
private static void show(final BufferedImage image)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
JFrame f = new JFrame();
f.getContentPane().add(new JLabel(new ImageIcon(image)));
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
});
}
}

Image won't draw, Canvas+double Buffering+Buffered Image used

Okay, so here's the deal: I'm trying to simply draw an object (in this case a tornado) onto my main canvas. I'm using JFrames, a canvas, a buffer strategy, and a buffered image to draw on. As far as I can figure I'm using the "game loop" correctly and from what I've been previously able to find my order of operations in the rendering is correct. Is there some thing I'm missing about the nature of using buffer strategies perhaps? I can't tell why i get just a grey screen.
Basically I'm trying to get this tornado program working using as many "proper" graphical and coding techniques as possible. Ultimately all I want is a little city that has a tornado moving through it, with each component existing as an object (the tornado, buildings, people, etc). However I find myself unable to continue util I can actually draw the blasted! The code below should show you whatever else you need to know, I'm fairly new to programming and this is a high school project, so any other pointers are welcome but mostly I want to know why the tornado wont draw!
I'm using eclipse, by the way. and so far I've followed these toutorials and posts to get where I am now:
http://www.gamedev.net/page/resources/_/technical/general-programming/java-games-active-rendering-r2418
http://examples.javacodegeeks.com/desktop-java/awt/image/drawing-on-a-buffered-image/
This is the main class:
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Transparency;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import javax.swing.*;
public class Main {
static BufferStrategy BuffStrat;
static Thread t1;
static BufferedImage backbuff;
static JFrame mainframe;
static Tornado tornado;
public static void main(String[] args) {
Simulation();
}
public static void Setup() {
mainframe = new JFrame("Tornado Ally");
mainframe.setIgnoreRepaint(true);
mainframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Canvas maincanvas = new Canvas();
maincanvas.setIgnoreRepaint(true);
maincanvas.setSize(750, 600);
mainframe.add(maincanvas);
mainframe.pack();
mainframe.setVisible(true);
maincanvas.createBufferStrategy(2);
BuffStrat = maincanvas.getBufferStrategy();
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice gd = ge.getDefaultScreenDevice();
GraphicsConfiguration gc = gd.getDefaultConfiguration();
backbuff = gc.createCompatibleImage(750, 600, Transparency.BITMASK);
tornado = new Tornado (0, 0, 0, 0, 0, backbuff);
}
public static void Simulation() {
Setup();
while (true) {
Render();
delay(10);
}
}
public static void Render() {
Graphics2D g = null;
g = backbuff.createGraphics();
g.setBackground(Color.LIGHT_GRAY);
g.clearRect(0, 0, 750, 600);
tornado.drawTornado(g);
Graphics gI = BuffStrat.getDrawGraphics();
gI.drawImage(backbuff, 0, 0, null);
BuffStrat.show();
gI.dispose();
g.dispose();
}
public static void delay(int ms) {
try {
Thread.sleep(ms);
} catch (InterruptedException e) {
}
}
}
This is the Tornado Object:
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.Image;
import java.awt.geom.Dimension2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class Tornado {
int x, y, z;
int mag;
double velocity;
Dimension2D hitbox;
GraphicsConfiguration gc;
Image t;
BufferedImage backbuff;
public Tornado(int x, int y, int z, int mag, double velocity, BufferedImage backbuff) {
this.x = x;
this.y = y;
this.z = z;
this.mag = mag;
this.velocity = velocity;
this.backbuff = backbuff;
{
try {
t = ImageIO.read(new File("Sprites.Tornado/TornadoFull.png"));
} catch (IOException e) {
}
}
}
public void drawTornado(Graphics2D g) {
g.drawImage(t, 0, 0, null);
}
}
When testing your code, it throw an exception because your code attempted to construct a BufferStrategy before the window had being resized. Because the main window is using a LayoutManager a better solution would be to use something like...
//maincanvas.setSize(750, 600);
maincanvas.setPreferredSize(new Dimension(750, 600));
When you call pack, the window will be resized to accommodate the preferred size of it's child components...
You are also ignoring your exceptions when you load you image, so you won't even know if it hadn't loaded for some reason. At the very least, you should be logging any exceptions...
For example...
try {
t = ImageIO.read(new File("TSprites.Tornado/TornadoFull.png"));
} catch (IOException e) {
e.printStackTrace();
}
This code suggests that the image is an external resource, if not (and it's built into the application), you would need to use getClass().getResource("/TSprites.Tornado/TornadoFull.png") instead of new File(...)...
You should also consider constructing your UI within the context of the Event Dispatching Thread, for details, take a look at Initial Threads.
For example, I changed your main method to look more like...
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
Simulation();
}
});
And updated your Simulation method to execute the main loop within the context of another thread, for example...
public static void Simulation() {
Setup();
new Thread(new Runnable() {
#Override
public void run() {
while (true) {
Render();
delay(10);
}
}
}).start();
}
You may also like to take a look at Code Conventions for the Java Programming Language as it will make you code easier to read by others
Also, beware of the over use of static. This can cause more problems then they solve if not used correctly...
Also, try and avoid the use of magic numbers, instead, use values you know, for example...
Instead of...
g.clearRect(0, 0, 750, 600);
Try using...
g.clearRect(0, 0, backbuff.getWidth(), backbuff.getHeight());

Non-opaque JButton background in non top level window becomes opaque?

Before you read, here are some clarifications on what the question is about:
The SSCCE is designed for Java 7. It would be possible to use sun.*.AWTUtilities to adapt it to Java 6, but it does not matter to me how it works on Java 6.
The faulting line is [...]new JDialog(someWindow). Ghosting can be fixed in the SSCCE by simply changing that line to [...]new JDialog().
Why don't top level windows exhibit ghosting?
Expected behavior: final JDialog d = new JDialog() (see SSCCE)
As you can see, the right window has a semitransparent background (as expected).
Actual behavior: final JDialog d = new JDialog(f) (see SSCCE)
In this case, the right window has an opaque background. As a matter of fact, it takes 3-4 repaints due to any reason (easiest to reproduce is repaint on rollover) for the background to become completely opaque.
SSCCE:
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.plaf.synth.ColorType;
import javax.swing.plaf.synth.Region;
import javax.swing.plaf.synth.SynthConstants;
import javax.swing.plaf.synth.SynthContext;
import javax.swing.plaf.synth.SynthLookAndFeel;
import javax.swing.plaf.synth.SynthPainter;
import javax.swing.plaf.synth.SynthStyle;
import javax.swing.plaf.synth.SynthStyleFactory;
public class SynthSSCCE
{
public static void main(String[] args) throws Exception
{
final SynthLookAndFeel laf = new SynthLookAndFeel();
UIManager.setLookAndFeel(laf);
SynthLookAndFeel.setStyleFactory(new StyleFactory());
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
final JFrame f = new JFrame();
{
f.add(new JButton("Works properly"));
f.setUndecorated(true);
f.setBackground(new Color(0, true));
f.setSize(300, 300);
f.setLocation(0, 0);
f.setVisible(true);
}
{
final JDialog d = new JDialog(f);
final JButton btn = new JButton("WTF?");
// uncomment and notice that this has no effect
// btn.setContentAreaFilled(false);
d.add(btn);
d.setUndecorated(true);
d.setBackground(new Color(0, true));
d.setSize(300, 300);
d.setLocation(320, 0);
d.setVisible(true);
}
}
});
}
static class StyleFactory extends SynthStyleFactory
{
private final SynthStyle style = new Style();
#Override
public SynthStyle getStyle(JComponent c, Region id)
{
return style;
}
}
static class Style extends SynthStyle
{
private final SynthPainter painter = new Painter();
#Override
protected Color getColorForState(SynthContext context, ColorType type)
{
if (context.getRegion() == Region.BUTTON && type == ColorType.FOREGROUND)
return Color.GREEN;
return null;
}
#Override
protected Font getFontForState(SynthContext context)
{
return Font.decode("Monospaced-BOLD-30");
}
#Override
public SynthPainter getPainter(SynthContext context)
{
return painter;
}
#Override
public boolean isOpaque(SynthContext context)
{
return false;
}
}
static class Painter extends SynthPainter
{
#Override
public void paintPanelBackground(SynthContext context, Graphics g, int x, int y, int w, int h)
{
final Graphics g2 = g.create();
try
{
g2.setColor(new Color(255, 255, 255, 128));
g2.fillRect(x, y, w, h);
}
finally
{
g2.dispose();
}
}
#Override
public void paintButtonBackground(SynthContext context, Graphics g, int x, int y, int w, int h)
{
final Graphics g2 = g.create();
try
{
if ((context.getComponentState() & SynthConstants.MOUSE_OVER) == SynthConstants.MOUSE_OVER)
g2.setColor(new Color(255, 0, 0, 255));
else
g2.setColor(new Color(0xAA, 0xAA, 0xAA, 255));
g2.fillRoundRect(x, y, w, h, w / 2, h / 2);
}
finally
{
g2.dispose();
}
}
}
}
And these are my questions...
What is going on? As in, why this exhibits behavior of a custom-painted non-opaque component that forgets to call super?
Why doesn't it happen to TL windows?
What is the easiest way to fix it, aside from not using non-TL windows?
it takes 3-4 repaints due to any reason (easiest to reproduce is repaint on rollover) for the background to become completely opaque.
Check out Backgrounds With Transparency which should give you some insight into the problem.
I've never played with Synth so I don't know if the same solution will work or not.
Why don't top level windows exhibit ghosting?
According to Oracle (Java Tutorials):
Each top-level container has a content pane that, generally speaking,
contains (directly or indirectly) the visible components in that
top-level container's GUI.
http://docs.oracle.com/javase/tutorial/uiswing/components/toplevel.html
The glass pane is often used to intercept input events occuring over the top-level container, and can also be used to paint over multiple components. It doesnt allow transparency.
Hence how you used
final Graphics g2 = g.create();
If you have the javax.swing.JComponent.paintComponent overrided in a method opposed to creating the graphics object yourself it should mitigate the transparency by super.g();
Fix this by creating a separate method listed above for graphics

Displaying Image in Java

I want to display an image but don't know what to do. Whether I have to install some library files or simply it can be done I don't know. Actually I want to do image processing, but first I have to take the image input and display image then I can get the effect of image processing as the output and decide whether it(algorithm) is correct or not. I have installed the eclipse only. I have searched in Google also but whatever they suggest is not working well. Either I have to install something or not.
I have tried the following code:
public class ImageTest {
public static void main(String[] args){
EventQueue.invokeLater(new Runnable() {
public void run(){
ImageFrame frame = new ImageFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
);
}
}
class ImageFrame extends JFrame{
public ImageFrame(){
setTitle("ImageTest");
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
ImageComponent component = new ImageComponent();
add(component);
getContentPane().validate();
getContentPane().repaint();
}
public static final int DEFAULT_WIDTH = 300;
public static final int DEFAULT_HEIGHT = 200;
}
class ImageComponent extends JComponent{
private static final long serialVersionUID = 1L;
private Image image;
public ImageComponent(){
try{
File image2 = new File("bishnu.jpg");
image = ImageIO.read(image2);
} catch (IOException e){
e.printStackTrace();
}
}
public void paintComponent (Graphics g){
if(image == null) return;
int imageWidth = image.getWidth(this);
int imageHeight = image.getHeight(this);
g.drawImage(image, 50, 50, this);
for (int i = 0; i*imageWidth <= getWidth(); i++)
for(int j = 0; j*imageHeight <= getHeight();j++)
if(i+j>0) g.copyArea(0, 0, imageWidth, imageHeight, i*imageWidth, j*imageHeight);
}
}
It simply shows a graphical window but can't show the image "bishnu.jpg"
Should I install anything in eclipse? But I think nothing needs to install.
import java.awt.FlowLayout;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
public class DisplayImage {
public static void main(String avg[]) throws IOException
{
DisplayImage abc=new DisplayImage();
}
public DisplayImage() throws IOException
{
BufferedImage img=ImageIO.read(new File("f://images.jpg"));
ImageIcon icon=new ImageIcon(img);
JFrame frame=new JFrame();
frame.setLayout(new FlowLayout());
frame.setSize(200,300);
JLabel lbl=new JLabel();
lbl.setIcon(icon);
frame.add(lbl);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
Running your code shows an image for me, after adjusting the path. Can you verify that your image path is correct, try absolute path for instance?
If you want to load/process/display images I suggest you use an image processing framework. Using Marvin, for instance, you can do that easily with just a few lines of source code.
Source code:
public class Example extends JFrame{
MarvinImagePlugin prewitt = MarvinPluginLoader.loadImagePlugin("org.marvinproject.image.edge.prewitt");
MarvinImagePlugin errorDiffusion = MarvinPluginLoader.loadImagePlugin("org.marvinproject.image.halftone.errorDiffusion");
MarvinImagePlugin emboss = MarvinPluginLoader.loadImagePlugin("org.marvinproject.image.color.emboss");
public Example(){
super("Example");
// Layout
setLayout(new GridLayout(2,2));
// Load images
MarvinImage img1 = MarvinImageIO.loadImage("./res/car.jpg");
MarvinImage img2 = new MarvinImage(img1.getWidth(), img1.getHeight());
MarvinImage img3 = new MarvinImage(img1.getWidth(), img1.getHeight());
MarvinImage img4 = new MarvinImage(img1.getWidth(), img1.getHeight());
// Image Processing plug-ins
errorDiffusion.process(img1, img2);
prewitt.process(img1, img3);
emboss.process(img1, img4);
// Set panels
addPanel(img1);
addPanel(img2);
addPanel(img3);
addPanel(img4);
setSize(560,380);
setVisible(true);
}
public void addPanel(MarvinImage image){
MarvinImagePanel imagePanel = new MarvinImagePanel();
imagePanel.setImage(image);
add(imagePanel);
}
public static void main(String[] args) {
new Example().setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
Output:
As a beginer, I found that is easy to see the picture you draw:
Source code
public class CheckCodeTest {
private int width = 100, height = 50;
private BufferedImage image = new BufferedImage(width, height,
BufferedImage.TYPE_INT_RGB);
#Test
public void drawGraphicsTest() throws IOException {
Graphics graphics = image.createGraphics();
// draw an orange rectangle
graphics.setColor(Color.orange);
graphics.fillRect(0,0,width,height);
// layout the picture right now!
graphics.drawImage(image,0,0,null);
ImageIO.write(image, "png", new File("checkcode.png"));
}
}
Output
It produce a picture file under your projects content.
output-picture
Then you can see what change after adding draw code in small window, it is more convenient than closing an jump-out Frame / Label window:
output-picture-in-editor
Hope it helps.

Categories

Resources