I also need to find a library which allows to implement the "chroma key" effect in Java. The video contains some part in green color, which is replaced which a picture during the rendering in order to create a new video.
I am linking my question with a similar question which was already answered but with uncomplete answer (Looking for Chromakey library in Java). Could you please specify how did you do to have something up and working so quickly? I have been unsuccessful for some months fighting against the same issue.
c00kiemon5ter pointed several resources:
JavaCV
JAI (Java Advanced Imaging API)
Java Image Processing Cookbook
Which one did work for you?
JavaCV contains lots of methods to process video streams.
This code should get you started: http://tech.groups.yahoo.com/group/OpenCV/message/59118 but it's probably too slow in Java. Try this approach:
Create a filter which turns all the green pixels into a mask (look for things that "select by color").
Use the mask to copy the background image into the video.
I would like to contribute with a piece of code which gave me quite good results. I wonder if I used the classes and methods that Aaron Digulla suggested.
Please note that in this case my video has black background, that is why I am replacing the black color with the background image. I expect to obtain better results when I can edit the video to have green background, because black color is more likely to be used within the video main characters and replacing wrong pixels causes a quite awful effect.
--
package transparentvideo;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.awt.image.FilteredImageSource;
import java.awt.image.ImageFilter;
import java.awt.image.ImageProducer;
import java.awt.image.RGBImageFilter;
import java.io.File;
import javax.media.Manager;
import javax.media.Player;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class TransparentVideo
{
Player mediaPlayer;
JFrame invisibleFrame;
JFrame visibleFrame;
JLabel videoScreen;
JPanel offScreenVideo;
String backgroundImageFile="nature.jpg";
String videoFile="girl.mov";
public TransparentVideo()
{
invisibleFrame = new JFrame();
invisibleFrame.setSize(400, 400);
Container container=invisibleFrame.getContentPane();
offScreenVideo = getvidComp();
offScreenVideo.setPreferredSize(container.getSize());
offScreenVideo.setVisible(true);
container.add(offScreenVideo);
invisibleFrame.setVisible(true);
visibleFrame=new JFrame();
visibleFrame.setSize(container.getSize());
visibleFrame.setLocationRelativeTo(null);
videoScreen = new JLabel();
visibleFrame.getContentPane().add(videoScreen);
visibleFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
visibleFrame.setVisible(true);
invisibleFrame.setVisible(false);
try
{
while(true)
{
if(mediaPlayer.getState()==Player.Started)
reDraw();
Thread.sleep(100);
}
}
catch (Exception ex)
{
System.out.println(ex.getMessage());
}
}
public void reDraw()
{
BufferedImage bi=new BufferedImage(videoScreen.getWidth(), videoScreen.getHeight(),
BufferedImage.TYPE_INT_ARGB);
bi.getGraphics().drawImage(new ImageIcon(backgroundImageFile).getImage(), 0 , 0 ,
videoScreen.getWidth(), videoScreen.getHeight(), null);
BufferedImage screenShot = createImage((JComponent) offScreenVideo,
new Rectangle(invisibleFrame.getBounds()));
Image im = makeColorTransparent(new ImageIcon(screenShot).getImage(), Color.BLACK);
bi.getGraphics().drawImage(im, 0 ,0 , videoScreen.getWidth(), videoScreen.getHeight(), null);
videoScreen.setIcon(new ImageIcon(bi));
}
public static BufferedImage createImage(Component component, Rectangle region)
{
if (!component.isDisplayable()) {
Dimension d = component.getSize();
if (d.width == 0 || d.height == 0) {
d = component.getPreferredSize();
component.setSize(d);
}
layoutComponent(component);
}
BufferedImage image = new BufferedImage(region.width, region.height, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = image.createGraphics();
if (!component.isOpaque())
{
g2d.setColor(component.getBackground());
g2d.fillRect(region.x, region.y, region.width, region.height);
}
g2d.translate(-region.x, -region.y);
component.paint(g2d);
g2d.dispose();
return image;
}
public static void layoutComponent(Component component)
{
synchronized (component.getTreeLock())
{
component.doLayout();
if (component instanceof Container)
{
for (Component child : ((Container) component).getComponents())
{
layoutComponent(child);
}
}
}
}
public JPanel getvidComp()
{
Manager.setHint(Manager.LIGHTWEIGHT_RENDERER,true);
try
{
mediaPlayer = Manager.createRealizedPlayer(new File(videoFile).toURL());
mediaPlayer.realize();
mediaPlayer.prefetch();
JPanel video=new JPanel(new BorderLayout());
video.add(mediaPlayer.getVisualComponent()) ;
mediaPlayer.start();
return video;
}
catch( Exception d)
{
return null;
}
}
public static Image makeColorTransparent( Image im, final Color color)
{
ImageFilter filter = new RGBImageFilter()
{
public int markerRGB = color.getRGB() | 0xFF000000;
public final int filterRGB(int x, int y, int rgb)
{
Color c=new Color(rgb);
int red=c.getRed();
int green=c.getGreen();
int blue=c.getBlue();
//if(red<140 && green<140 && blue<140)
{
int alpha=Math.max(Math.max(red , green), Math.max(green, blue))*3;
c=new Color(red , green , blue , alpha>255 ?255 :alpha );
}
return c.getRGB();
}
};
ImageProducer ip = new FilteredImageSource(im.getSource(), filter);
return Toolkit.getDefaultToolkit().createImage(ip);
}
public static void main(String[] args) {
new TransparentVideo();
}
}
Related
I want to make a java application that mirrors the screen of a computer (with Windows 10) and shows it in itself. It's nothing too complex (I think): it's just an app that captures everything that is showed on the computer screen and shows it in itself. But I don't want to mirror the main screen. I want to mirror the secondary screen, the projected screen.
---> Reason why I need this:
I project contents on big screens in church which are almost 20 meters away from me and it's a bit hard to see the content, and there's no space to put another monitor next to mine... That's why I need an app that shows me in the main screen what is being exhibited.
I already searched about it on web and didn't find satisfactory results... The maximum I found was screenshot and literally record the screen and save as MP4 or another extension of your preference.
Someone could help me in this, please?
Conceptually, the idea is pretty simple. You want to use an instance of Robot to capture a snapshot of the screen.
The first thing you need to do though, is get the "area" of the screen you want to capture and this is not as easy it as might sound as Java doesn't seem to provide the "screen names" 🤨 (on Windows GraphicsDevice#getIDstring might return the name, but on MacOS it didn't)
So, I started by trying to look at the screen resolutions and tried to figure out which screen I wanted...
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice lstGDs[] = ge.getScreenDevices();
for (GraphicsDevice gd : lstGDs) {
System.out.println(gd.getDisplayMode());
}
I could have also used getBounds and tried to figure out where each screen was positioned, but what ever works.
Once you have this information, you can use GraphicsDevice#getBounds to get the physical area of the screen, which you can feed to Robot
Now, you need some way to produce and consume those snapshots. You need to be aware of the fact that Swing is not thread safe and is single threaded. This means you can't capture the snapshot from the Event Dispatching Thread and should not try to update the UI from outside of the Event Dispatching Thread. See Concurrency in Swing for more details.
With this in mind, I decided to go with a SwingWorker. It does most of the heavy lifting for us and provides a simplified workflow. See Worker Threads and SwingWorker for more details.
Okay, the next issue to solve, is how to render the screen. The basic concept is just to paint the image onto a component, which isn't that hard, the hard part is scaling the image.
There's been a lot of discussion on the subject, but you could take a look at How do I resize images inside an application when the application window is resized? and Java: maintaining aspect ratio of JPanel background image
The first presents a concept of "scale to fit" and "scale to fill" algorithms and the second presents a better way to scale a image then using Image#getScaledInstance (which the example uses) as getScaledInstance doesn't always present the best result. You could also take a look at Quality of Image after resize very low -- Java which presents some more ideas and discussions.
import java.awt.AWTException;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.image.BufferedImage;
import java.time.Duration;
import java.time.Instant;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingWorker;
class Main {
public static void main(String[] args) {
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice lstGDs[] = ge.getScreenDevices();
for (GraphicsDevice gd : lstGDs) {
System.out.println(gd.getDisplayMode());
}
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private CaptureWorker worker;
private BufferedImage snapshot;
public TestPane() {
}
#Override
public void addNotify() {
super.addNotify();
startCapture();
}
#Override
public void removeNotify() {
super.removeNotify();
stopCapture();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
protected void startCapture() {
try {
stopCapture();
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice gd = ge.getScreenDevices()[0];
worker = new CaptureWorker(gd, new CaptureWorker.Observer() {
#Override
public void imageAvaliable(CaptureWorker source, BufferedImage img) {
TestPane.this.snapshot = img;
repaint();
}
});
worker.execute();
} catch (AWTException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
}
protected void stopCapture() {
if (worker == null) {
return;
}
worker.stop();
worker = null;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
if (snapshot != null) {
double scaleFactor = Math.min(1d, getScaleFactorToFill(new Dimension(snapshot.getWidth(), snapshot.getHeight()), getSize()));
int scaleWidth = (int) Math.round(snapshot.getWidth() * scaleFactor);
int scaleHeight = (int) Math.round(snapshot.getHeight() * scaleFactor);
Image imageToRender = snapshot.getScaledInstance(scaleWidth, scaleHeight, Image.SCALE_SMOOTH);
int x = (getWidth() - imageToRender.getWidth(this)) / 2;
int y = (getHeight() - imageToRender.getHeight(this)) / 2;
g2d.drawImage(imageToRender, x, y, this);
}
g2d.dispose();
}
public double getScaleFactor(int iMasterSize, int iTargetSize) {
double dScale = 1;
dScale = (double) iTargetSize / (double) iMasterSize;
return dScale;
}
public double getScaleFactorToFit(Dimension original, Dimension toFit) {
double dScale = 1d;
if (original != null && toFit != null) {
double dScaleWidth = getScaleFactor(original.width, toFit.width);
double dScaleHeight = getScaleFactor(original.height, toFit.height);
dScale = Math.min(dScaleHeight, dScaleWidth);
}
return dScale;
}
public double getScaleFactorToFill(Dimension masterSize, Dimension targetSize) {
double dScaleWidth = getScaleFactor(masterSize.width, targetSize.width);
double dScaleHeight = getScaleFactor(masterSize.height, targetSize.height);
double dScale = Math.max(dScaleHeight, dScaleWidth);
return dScale;
}
}
public class CaptureWorker extends SwingWorker<Void, BufferedImage> {
public interface Observer {
public void imageAvaliable(CaptureWorker source, BufferedImage img);
}
private AtomicBoolean keepRunning = new AtomicBoolean(true);
private Robot bot;
private Rectangle captureBounds;
private final Duration interval = Duration.ofMillis(250);
private Observer observer;
public CaptureWorker(GraphicsDevice device, Observer observer) throws AWTException {
captureBounds = device.getDefaultConfiguration().getBounds();
this.observer = observer;
bot = new Robot();
}
public void stop() {
keepRunning.set(false);
}
#Override
protected void process(List<BufferedImage> chunks) {
BufferedImage img = chunks.get(chunks.size() - 1);
observer.imageAvaliable(this, img);
}
#Override
protected Void doInBackground() throws Exception {
try {
while (keepRunning.get()) {
Instant anchor = Instant.now();
System.out.println("Snapshot");
BufferedImage image = bot.createScreenCapture(captureBounds);
System.out.println("Pubish");
publish(image);
Duration duration = Duration.between(anchor, Instant.now());
System.out.println("Took " + duration.toMillis());
duration = duration.minus(interval);
System.out.println("Time remaining " + duration.toMillis());
if (duration.isNegative()) {
long sleepTime = Math.abs(duration.toMillis());
System.out.println("Sleep for " + sleepTime);
Thread.sleep(sleepTime);
}
}
} catch (Exception exp) {
exp.printStackTrace();
}
return null;
}
}
}
Don't forget
You need to know which screen you want to capture. Take a look at the startCapture method, this where I configured the CaptureWorker. I just grabbed the screen at index 0 and was lucky enough to get the screen I wanted 🤪
Also, this is unsupported. This presents the "how to capture a screen and renderer it a window" portion of your question. How you decide which screen or how you might present that information to the user to configure is all up to you
I am working on a simple 2D game. Each tick, I want to check an effects queue that will start a thread for a certain effect(fading transitions, audio fade in and out, etc). For example, pressing "Play" on the menu screen will add a "FadeOut" message to this queue, which will be processed and start a thread to draw a black rectangle with an increasing alpha value over my GamePanel.
I'm overriding paintComponent() and sending my Graphics object to my GameStateManager, which passes along the Graphics object to the current states' draw(). I currently don't have an effects state (which maybe I should) to route the paintComponent() graphics object to, but I do pass my gamepanel to my effects thread, where I can use getGraphics() to draw on it. Drawing a rectangle to the GamePanel directly just causes flickering, as the gameloop is still rendering the game.
I found I can draw a black rectangle with increasing alpha to a BufferedImage, set the composite to AlphaComposite.Src (which causes the new draw to replace the old) then draw the BufferedImage over the game panel. The problem is the BufferedImages drawn to the game panel don't get overridden each draw, so the fade out happens really quickly because these black BufferedImages of various alphas just stack on each other.
I wrote this short program to test composite settings and see what is getting overridden. All drawing is done in the draw(), which would be my run() in the effects thread.
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class ScratchPad extends JPanel implements Runnable
{
private JFrame oFrame = null;
private Thread oGameThread = null;
private Graphics2D oPanelGraphics = null;
private Graphics2D oImageGraphics = null;
private BufferedImage oImage = null;
public static void main(String args[]) throws Exception
{
new ScratchPad();
}
public ScratchPad()
{
createFrame();
initPanel();
addAndShowComponents();
oGameThread = new Thread(this, "Game_Loop");
oGameThread.start();
}
private void addAndShowComponents()
{
oFrame.add(this);
oFrame.setVisible(true);
}
private void initPanel()
{
this.setOpaque(true);
this.setBackground(Color.cyan);
}
private void createFrame()
{
oFrame = new JFrame("Fade");
oFrame.setSize(700, 300);
oFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
oFrame.setLocationRelativeTo(null);
}
public void run()
{
oImage = new BufferedImage(200, 200, BufferedImage.TYPE_INT_ARGB);
while(true)
{
try
{
draw();
Thread.sleep(100);
}
catch(InterruptedException e)
{
}
}
}
private void draw()
{
oPanelGraphics = (Graphics2D)this.getGraphics();
oImageGraphics = oImage.createGraphics();
oImageGraphics.setComposite(AlphaComposite.Src);
oImageGraphics.setColor(new Color(0,0,0,90));
oImageGraphics.fillRect(0, 0, oImage.getWidth(), oImage.getHeight());
oPanelGraphics.drawImage(oImage, 10, 10, null);
oImageGraphics.setColor(new Color(0,0,0,60));
oImageGraphics.fillRect(0, 0, oImage.getWidth(), oImage.getHeight());
oPanelGraphics.drawImage(oImage, 220, 10, null);
oImageGraphics.setColor(new Color(0,0,0,30));
oImageGraphics.fillRect(0, 0, oImage.getWidth(), oImage.getHeight());
oPanelGraphics.drawImage(oImage, 430, 10, null);
// Drawing this image over location of first image, should overwrite first
// after setting composite to 'Src'
oPanelGraphics.setComposite(AlphaComposite.Src);
oImageGraphics.setColor(new Color(0,0,0,10));
oImageGraphics.fillRect(0, 0, oImage.getWidth(), oImage.getHeight());
oPanelGraphics.drawImage(oImage, 10, 10, null);
oImageGraphics.dispose();
oPanelGraphics.dispose();
}
} // end class
What's interesting is setting the composite on 'oPanelGraphics' causes any alpha to the BufferedImage to go away, resulting in a fully opaque black image being drawn over the image that was previously there. Even setting the color to something other than black doesn't have an effect.
What's also interesting is setting the composite for the BufferedImage to:
oImageGraphics.setComposite(AlphaComposite.SrcIn);
causes nothing to be shown. The Oracle documentation on compositing graphics in Java2D states this for 'SrcIn':
"If pixels in the source and the destination overlap, only the source pixels in the overlapping area are rendered."
So, I would expect this to have the same behavior I get with AlphaComposite.Src.
Maybe someone out there can shed some light on whats going on with these composites, and how I could achieve my desired effect.
There are a number issues with what you "seem" to be trying to do
Don't call getGraphics on a component. This can return null and only returns a snapshot of what was last painted during a Swing paint cycle. Anything you paint to it will be erased on the next paint cycle
You should also never dispose of Graphics context you did not create, doing so could effect other components that are painted by Swing
Painting is compounding, this means that painting to the same Graphics context (or BufferedImage) over and over again, will continue to apply those changes over the top of what was previously painted
You also don't seem to have a concept of how animation should work. Instead of trying to paint your fade effect in a single pass, where the results can't be applied to the screen, you need to apply a phase on each cycle and allow that to be updated to the screen before the next pass runs.
The following is a really basic example of what I'm talking about. It takes a "base" image (this could be the "base" state of the game, but I've used a static image) and the paints effects over the top.
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private Engine engine;
private Image frame;
public TestPane() {
engine = new Engine();
engine.setEngineListener(new EngineListener() {
#Override
public void updateDidOccur(Image img) {
frame = img;
repaint();
}
});
engine.start();
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
engine.addEffect(new FadeOutEffect(Color.BLACK));
}
});
}
#Override
public Dimension getPreferredSize() {
return engine.getSize();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (frame != null) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.drawImage(frame, 0, 0, null);
g2d.dispose();
}
}
}
public interface EngineListener {
public void updateDidOccur(Image img);
}
public class Engine {
// This is the "base" image, without effects
private BufferedImage base;
private Timer timer;
private EngineListener listener;
private List<Effect> effects = new ArrayList<Effect>(25);
public Engine() {
try {
base = ImageIO.read(new File("/Volumes/Big Fat Extension/Dropbox/MegaTokyo/megatokyo_omnibus_1_3_cover_by_fredrin-d4oupef 50%.jpg"));
} catch (IOException ex) {
ex.printStackTrace();
}
timer = new Timer(10, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
int width = base.getWidth();
int height = base.getHeight();
BufferedImage frame = new BufferedImage(width, height, base.getType());
Graphics2D g2d = frame.createGraphics();
g2d.drawImage(base, 0, 0, null);
Iterator<Effect> it = effects.iterator();
while (it.hasNext()) {
Effect effect = it.next();
if (!effect.applyEffect(g2d, width, height)) {
it.remove();
}
}
g2d.dispose();
if (listener != null) {
listener.updateDidOccur(frame);
}
}
});
}
public void start() {
timer.start();
}
public void stop() {
timer.stop();
}
public void addEffect(Effect effect) {
effects.add(effect);
}
public void setEngineListener(EngineListener listener) {
this.listener = listener;
}
public Dimension getSize() {
return base == null ? new Dimension(200, 200) : new Dimension(base.getWidth(), base.getHeight());
}
}
public interface Effect {
public boolean applyEffect(Graphics2D context, int width, int height);
}
public class FadeOutEffect implements Effect {
private int tick = 0;
private Color fadeToColor;
public FadeOutEffect(Color fadeToColor) {
this.fadeToColor = fadeToColor;
}
#Override
public boolean applyEffect(Graphics2D context, int width, int height) {
tick++;
float alpha = (float) tick / 100.0f;
if (alpha > 1.0) {
return false;
}
Graphics2D g2d = (Graphics2D) context.create();
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha));
g2d.setColor(fadeToColor);
g2d.fillRect(0, 0, width, height);
g2d.dispose();
return true;
}
}
}
Remember, every effect or change should be applied within the same "main loop", this means you shouldn't have multiple threads, in fact, since Swing is not thread safe, you should avoid having any additional threads if possible. This example make use of a Swing Timer to act as the "main loop" because the ActionListers actionPerformed method is called within the context of the EDT, making it safe to update the UI from. It also provides a simple synchronisation method, as the UI can't be painted while the actionPerformed method is been called
I have object of javafx.scene.image.Image class. How can I print it on printer using javafx8? Please, note, that I don't want to print some node, for example ImageView. I need to print image. Although it's very simple question I can't find answer in internet.
The only code I found is:
PrinterJob job = PrinterJob.createPrinterJob();
if (job != null) {
boolean success = job.printPage(node);
if (success) {
job.endJob();
}
}
However it is about printing the node.
Problem
javafx.print.PrinterJob only prints Node and it's subclasses. Image isn't a subclass of Node. So you have to wrap it in a Node (ImageView) or print from plain Java.
Difference of JavaFX-PrinterJob and AWT-PrinterJob
The main difference is, that the JavaFX PrinterJob was introduced for usage with Node objects. It has set some usefull things as a JavaFX Property like the Jobstatus or the Printer itself. And it is more thread safe as the older AWT PrinterJob. The AWT PrinterJob can print mostly anything you want like Strings, Images, Arc's, etc., because it takes an AWT Graphics Object to draw things on the page.
Solution
Before you can use the plain Java solution, you have to convert your FX-Image to a BufferedImage with SwingFXUtils.fromFXImage(). But there is a bug with *.jpg Files, as described here: https://stackoverflow.com/a/30995307/4170073
The Minimal, Complete, and Verifiable example down below shows a working solution:
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import javafx.application.Application;
import javafx.embed.swing.SwingFXUtils;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.image.Image;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class ImagePrinter extends Application {
#Override
public void start(Stage primaryStage) {
Image image = new Image("http://www.gnu.org/graphics/gnu-head.png");
BufferedImage bufferedImage = SwingFXUtils.fromFXImage(image, null);
Button btn = new Button();
btn.setText("Print Image");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
printImage(bufferedImage);
}
});
StackPane root = new StackPane();
root.getChildren().add(btn);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Image Printer");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
private void printImage(BufferedImage image) {
PrinterJob printJob = PrinterJob.getPrinterJob();
printJob.setPrintable(new Printable() {
#Override
public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException {
// Get the upper left corner that it printable
int x = (int) Math.ceil(pageFormat.getImageableX());
int y = (int) Math.ceil(pageFormat.getImageableY());
if (pageIndex != 0) {
return NO_SUCH_PAGE;
}
graphics.drawImage(image, x, y, image.getWidth(), image.getHeight(), null);
return PAGE_EXISTS;
}
});
try {
printJob.print();
} catch (PrinterException e1) {
e1.printStackTrace();
}
}
}
I encountered a problem while I am trying to display an image after I clicked a button and chose image file within the "Choose File Dialog".
Initially, I was managed to display the chosen image in JLabel, but later I created a separate ActionListener, I think it started to go wrong since then. Whatever image I choose, the JLabel won't display it.
I debugged it, and sure that the file chooser does pass the image to ImageIcon, JLabel does get the value from ImageIcon, but it doesn't display the image even after revalidate() and repaint().
Here I attached my code for your kind reference!
(I trimmed the code for a clean look, so there might be some brackets left not useful)
package com.xxx.LoyalCardManager;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JSeparator;
import javax.swing.JTextField;
import javax.swing.filechooser.FileFilter;
public class LoyalCardManagerMain implements ActionListener{
private JFrame frame;
private DatabaseHandler db = new DatabaseHandler();
private JLabel labelPic;
private JButton buttonPic;
private File picFile = new File("");
private BufferedImage image;
/**
* Launch the application.
* #throws SQLException
* #throws ClassNotFoundException
*/
public static void main(String[] args) throws SQLException, ClassNotFoundException {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
LoyalCardManagerMain window = new LoyalCardManagerMain();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
/**
* Create the application.
*/
public LoyalCardManagerMain() {
// Database initialisation
initDatabase();
// Draw GUI
frame = new JFrame();
frame.setBounds(100, 100, 619, 487);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(null);
buttonPic = new JButton("Click to Choose Pic");
buttonPic.setBounds(415, 252, 166, 29);
frame.getContentPane().add(buttonPic);
buttonPic.setEnabled(false);
buttonPic.setActionCommand("ChoosePic");
buttonPic.addActionListener(this);
labelPic = new JLabel();
labelPic.setBounds(415, 30, 167, 210);
frame.getContentPane().add(labelPic);
}
public void actionPerformed(ActionEvent event) {
String command = event.getActionCommand();
if (command.equals("ChoosePic")) {
//TODO Label now cannot display images.
JFileChooser chooser = new JFileChooser();
chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
chooser.setAcceptAllFileFilterUsed(false);
chooser.setFileFilter(new FileFilter() {
public boolean accept (File f) {
String extension = Utils.getExtension(f);
if(extension != null) {
if (extension.equals(Utils.gif) ||
extension.equals(Utils.jpeg) ||
extension.equals(Utils.jpg) ||
extension.equals(Utils.png) ||
extension.equals(Utils.tif) ||
extension.equals(Utils.tiff)) {
return true;
}else{
return false;
}
}
return false;
}
public String getDescription() {
return "Image File (*.gif, *.jpeg, *.jpg, *.png, *.tif, *.tiff)";
}
});
int retVal = chooser.showOpenDialog(frame);
if (retVal == JFileChooser.APPROVE_OPTION) {
picFile = chooser.getSelectedFile();
try {
image = ImageIO.read(picFile);
} catch (IOException e) {
e.printStackTrace();
}
// Calculate the pic's ratio and do re-scale
double ratio = (double) labelPic.getWidth() / (double) labelPic.getHeight();
// Do image scale, scaledW is the new Width, and LabelPic.getHeight is the new Height.
int scaledW = (int) (image.getHeight() * ratio);
image = new BufferedImage(scaledW, labelPic.getHeight(), BufferedImage.SCALE_FAST);
ImageIcon icon = new ImageIcon(image);
labelPic.setVisible(true);
labelPic.setIcon(icon);
labelPic.revalidate();
labelPic.repaint();
}
}
}
}
I also referenced other similar questions:
image loading using a JFileChooser into a JFrame
Image won't display in JLabel
Updating an image contained in a JLabel - problems
External Site: JFIleChooser opening image to JLabel
As well as Java Tutorial Docs
How to Use Buttons, Check Boxes, and Radio Buttons
But I still can't figure it out why the JLabel not display the chosen image.
Thanks for your kind help mates!
Ok, I finally figured out what's wrong with the code:
If I intend to use BufferedImage to resize (sorry, in my question I mis-understanding the method scale with resize), I need to use drawImage method to "redraw" the image. Otherwise the image will not be shown.
I made modification here:
double ratio = (double) labelPic.getWidth() / (double) labelPic.getHeight();
// Do image scale, scaledW is the new Width, and LabelPic.getHeight is the new Height.
int scaledW = (int) (image.getHeight() * ratio);
image = new BufferedImage(scaledW, labelPic.getHeight(), BufferedImage.SCALE_FAST);// Edit here
ImageIcon icon = new ImageIcon(image);
labelPic.setVisible(true);
labelPic.setIcon(icon);
labelPic.revalidate();
labelPic.repaint();
From the "Edit Here" mark, I use the following code:
BufferedImage imageTemp = new BufferedImage(resizedW, resizedH, BufferedImage.TYPE_INT_RGB);
imageTemp.getGraphics().drawImage(image,0,0, scaledW, scaledH, null);
image = imageTemp;
And there's difference between first pass the value to imageTemp then pass to image and directly pass the value to image. If I pass the new BufferedImage directly to image, it will display a pure black colour instead of the image you choose.
Try using this to display the image:
JfileChooser getImage = new JFileChooser();
..........
ImageIcon imagePath= new ImageIcon(getImage.getPath());
JLabel imageLabel= new JLabel() {
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(imagePath.getImage(), 0, 0, width, height, null);
}
};
imageLabel.setLocation(10, 40);
imageLabel.setBorder(viewAnimalPanelBorder);
imageLabel.setSize(200, newHeight);
panel.add(imageLabel);
Let me know if you require more assistance.
Also, try displaying the picture without using the JFileChooser, maybe hard code the path for a test.
I have am writing a small game using Applet. I want to be able to check the color of a pixel on the screen. However, when I use .getRGB() on my buffered image in my game loop (a while loop that executes over and over), it gives me inconsistent values even if the pixel color never actually changes!
For example, if I fill the image with green color and I call .getRGB() on a pixel in the middle of the screen sometimes it gives me 0xFF00FF00 (green) as the color, other times it gives me 0xFF000000 (black) even if the color always stays green!
Any help?
Here's the relevant code if that helps, I've minimized it just to focus on the problem:
import java.applet.Applet;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
public class t extends Applet implements Runnable {
Graphics2D bufferG;
BufferedImage bufferI;
final int WIDTH = 500, HEIGHT = 500;
public void init() {
setSize(WIDTH, HEIGHT);
bufferI = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
bufferG = bufferI.createGraphics();
(new Thread(this)).start();
}
public void run() {
while (true){
if (bufferI.getRGB(WIDTH/2, HEIGHT/2)==0xFF000000) System.out.println("BLACK");
}
}
public void paint(Graphics g) {
bufferG.setColor(Color.green);
bufferG.fillRect(0, 0, WIDTH, HEIGHT);
g.drawImage(bufferI, 0, 0, this);
}
}
I'm not sure why you are dealing with buffers. This paints the image green & produces no output on the command line (reporting the color as black).
// <applet code='t' width=400 height=200></applet>
import java.applet.Applet;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
public class t extends Applet implements Runnable {
BufferedImage bufferI;
final int WIDTH = 500, HEIGHT = 500;
public void init() {
bufferI = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
Graphics g = bufferI.getGraphics();
g.setColor(Color.GREEN);
g.fillRect(0,0,WIDTH,HEIGHT);
g.dispose();
(new Thread(this)).start();
}
public void run() {
while (true){
if (bufferI.getRGB(WIDTH/2, HEIGHT/2)==0xFF000000) System.out.println("BLACK");
}
}
public void paint(Graphics g) {
g.drawImage(bufferI, 0, 0, this);
}
}
Notes
To compile & run
prompt> javac t.java
prompt> appletviewer t.java // yes that file extension is correct.
Size
The size of an applet is set in the HTML that loads it, it should not attempt to set its own size.