I´m trying to copy a png file to clipboard within a program and maintain its alpha channel when pasted in another program (e.g. ms office, paint, photoshop). The problem is, that the alpha channel turns black in most of the programs. I've been searching the web for hours now and can't find a solution. The Code I'm using:
setClipboard(Toolkit.getDefaultToolkit().getImage(parent.getSelectedPicturePath()));
public static void setClipboard(Image image) {
ImageSelection imgSel;
if (OSDetector.isWindows()) {
imgSel = new ImageSelection(image);
} else {
imgSel = new ImageSelection(getBufferedImage(image));
}
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(imgSel, null);
}
Is there any way to maintain the alpha channel in Java? I've tried converting the png to BufferedImage, Image, etc. and the pasting it to the clipboard, but nothing works.
Assuming that OSDetector is working properly, I was able to get the OP's code to work out of the box on Windows Server 2008R2 64-bit running Oracle JDK 1.8.0_131. The OP omitted the code for getBufferedImage(), however I suspect it was some variant of the version from this blog.
When I tested the code using the blog's version of getBufferedImage() on Windows (ignoring the OSDetector check), I was able to reproduce a variant of the issue where the entire image was black, which turned out to be a timing issue with the asynchronous calls to Image.getWidth(), Image.getHeight(), and Graphics.drawImage(), all of which return immediately and take an observer for async updates. The blog code passes null (no observer) for all of these invocations, and expects results to be returned immediately, which was not the case when I tested.
Once I modified getBufferedImage() to use callbacks, I reproduced the exact issue: alpha channels appear black. The reason for this behavior is that the image with the transparency is drawn onto a graphics context that defaults to a black canvas. What you are seeing is exactly what you would see if you viewed the image on a web page with a black background.
To change this, I used a hint from this StackOverflow answer and painted the background white.
I used the ImageSelection implementation from this site, which simply wraps an Image instance in a Transferrable using DataFlavor.imageFlavor.
Ultimately for my tests, both the original image and the buffered image variants worked on Windows. Below is the code:
public static void getBufferedImage(Image image, Consumer<Image> imageConsumer) {
image.getWidth((img, info, x, y, w, h) -> {
if (info == ImageObserver.ALLBITS) {
BufferedImage buffered = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
Graphics2D g2 = buffered.createGraphics();
g2.setColor(Color.WHITE); // You choose the background color
g2.fillRect(0, 0, w, h);
if (g2.drawImage(img, 0, 0, w, h, (img2, info2, x2, y2, w2, h2) -> {
if (info2 == ImageObserver.ALLBITS) {
g2.dispose();
imageConsumer.accept(img2);
return false;
}
return true;
})) {
g2.dispose();
imageConsumer.accept(buffered);
}
return false;
}
return true;
});
}
public static void setClipboard(Image image) {
boolean testBuffered = true; // Both buffered and non-buffered worked for me
if (!testBuffered) {
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new ImageSelection(image), null);
} else {
getBufferedImage(image, (buffered) -> {
ImageSelection imgSel = new ImageSelection(buffered);
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(imgSel, null);
});
}
}
I hope this helps. Best of luck.
Is this right answer? Have you tried this?
public void doCopyToClipboardAction()
{
// figure out which frame is in the foreground
MetaFrame activeMetaFrame = null;
for (MetaFrame mf : frames)
{
if (mf.isActive()) activeMetaFrame = mf;
}
// get the image from the current jframe
Image image = activeMetaFrame.getCurrentImage();
// place that image on the clipboard
setClipboard(image);
}
// code below from exampledepot.com
//This method writes a image to the system clipboard.
//otherwise it returns null.
public static void setClipboard(Image image)
{
ImageSelection imgSel = new ImageSelection(image);
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(imgSel, null);
}
// This class is used to hold an image while on the clipboard.
static class ImageSelection implements Transferable
{
private Image image;
public ImageSelection(Image image)
{
this.image = image;
}
// Returns supported flavors
public DataFlavor[] getTransferDataFlavors()
{
return new DataFlavor[] { DataFlavor.imageFlavor };
}
// Returns true if flavor is supported
public boolean isDataFlavorSupported(DataFlavor flavor)
{
return DataFlavor.imageFlavor.equals(flavor);
}
// Returns image
public Object getTransferData(DataFlavor flavor)
throws UnsupportedFlavorException, IOException
{
if (!DataFlavor.imageFlavor.equals(flavor))
{
throw new UnsupportedFlavorException(flavor);
}
return image;
}
}
Source : http://alvinalexander.com/java/java-copy-image-to-clipboard-example
I'm not tried this myself and I'm not sure about that. Hopefully you get right answer.
Here is a very simple, self contained example that works. Reading or creating the image is up to you. This code just creates a red circle drawn on an alpha-type BufferedImage. When I paste it in any program that supports transparency, it shows correctly. Hope it helps.
import java.awt.*;
import java.awt.datatransfer.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
public class CopyImageToClipboard {
public void createClipboardImageWithAlpha() {
//Create a buffered image of the correct type, with alpha.
BufferedImage image = new BufferedImage(600, 600, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = image.createGraphics();
//Draw in the buffered image.
g2d.setColor(Color.red);
g2d.fillOval(10, 10, 580, 580);
//Add the BufferedImage to the clipboard with transferable image flavor.
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
Transferable transferableImage = getTransferableImage(image);
clipboard.setContents(transferableImage, null);
}
private Transferable getTransferableImage(final BufferedImage bufferedImage) {
return new Transferable() {
#Override
public DataFlavor[] getTransferDataFlavors() {
return new DataFlavor[] { DataFlavor.imageFlavor };
}
#Override
public boolean isDataFlavorSupported(DataFlavor flavor) {
return DataFlavor.imageFlavor.equals(flavor);
}
#Override
public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
if (DataFlavor.imageFlavor.equals(flavor)) {
return bufferedImage;
}
return null;
}
};
}
}
Related
I am using a combination of PDFBox with Apache Batik in order to render PDF pages as SVG documents. Most of them work fine, but I have some issues when rendering specific images into SVG.
This is the code I use. It is mostly based on the post over there.
public void extractBookSvg(File pdfFile) throws Exception {
// ... preliminary business actions
SVGGeneratorContext ctx = createContext();
SVGGraphics2D g = null;
try (PDDocument document = PDDocument.load(pdfFile, MemoryUsageSetting.setupMixed(2147483648l))) {
PDFRenderer renderer = new PDFRenderer(document);
long startTime = System.currentTimeMillis();
int pageNr = 0;
for (PDPage page : document.getPages()) {
long startTimeForPage = System.currentTimeMillis();
g = createGraphics(ctx);
renderer.renderPageToGraphics(pageNr, g, 3.47222f);
pageNr++;
try (OutputStream os = new ByteArrayOutputStream();
Writer out = new OutputStreamWriter(os)) {
g.stream(out, true);
//... do other business actions
}
}
}
finally {
pdfFile.delete();
if (g != null) {
g.finalize();
g.dispose();
}
}
}
private SVGGraphics2D createGraphics(SVGGeneratorContext ctx) {
SVGGraphics2D g2d = new CustomSVGGraphics2D(ctx, false);
return g2d;
}
private SVGGeneratorContext createContext() {
DOMImplementation impl = GenericDOMImplementation.getDOMImplementation();
String svgNS = "http://www.w3.org/2000/svg";
Document myFactory = impl.createDocument(svgNS, "svg", null);
SVGGeneratorContext ctx = SVGGeneratorContext.createDefault(myFactory);
return ctx;
}
public static class CustomSVGGraphics2D extends SVGGraphics2D {
public CustomSVGGraphics2D(SVGGeneratorContext generatorCtx, boolean textAsShapes) {
super(generatorCtx, textAsShapes);
}
#Override
public GraphicsConfiguration getDeviceConfiguration() {
return new CustomGraphicsConfiguration();
}
}
private static final class CustomGraphicsConfiguration extends GraphicsConfiguration {
#Override
public AffineTransform getNormalizingTransform() {
return null;
}
#Override
public GraphicsDevice getDevice() {
return new CustomGraphicsDevice();
}
#Override
public AffineTransform getDefaultTransform() {
return null;
}
#Override
public ColorModel getColorModel(int transparency) {
return null;
}
#Override
public ColorModel getColorModel() {
return null;
}
#Override
public java.awt.Rectangle getBounds() {
return null;
}
}
private static final class CustomGraphicsDevice extends GraphicsDevice {
#Override
public int getType() {
return 0;
}
#Override
public String getIDstring() {
return null;
}
#Override
public GraphicsConfiguration[] getConfigurations() {
return null;
}
#Override
public GraphicsConfiguration getDefaultConfiguration() {
return null;
}
}
As mentioned above, the issue comes when rendering images: they either do not get rendered at all (show up as black boxes), or on some images that have opacity lower than 1, they are shown with opacity 1.
Here is an example of both cases:
PDF render of transparent image
SVG render of transparent image
These images do not get rendered at all in SVG
How they actually show in the SVG
However, if I directly render those pages as images (using BufferedImage instead Graphics2D), they both render fine (with a lower quality than the svg, of course).
Also, I have tried debugging the PDF with the PDFDebugger utility, and those images do not appear in the resources XObject list of the page, and I can't seem to find them nowhere else.
My questions are:
how can I find out if the issue comes from the way PDFBox renders into the Graphics2D object, or it comes from the way Batik does the SVG generation?
is there a way to make sure those images are properly displayed at generation time? Because I see no errors in the logs.
if so, what could be the solution?
Thank you!
i created a custom Java Button like this
public class GraphicPaintedButton extends JButton implements ToPaint{
protected BufferedImage background;
private boolean painted = false;
public GraphicPaintedButton() {
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
if(painted){
System.out.println("PAINTING!");
Dimension size = getSize();
g.drawImage(background, 0, 0,size.width, size.height,0, 0, background.getWidth(), background.getHeight(), null);
}
}
#Override
public void loadImage(String url){
painted = true;
URL imagePath = getClass().getResource(url);
BufferedImage result = null;
try {
result = ImageIO.read(imagePath);
} catch (IOException | IllegalArgumentException e) {
System.err.println("Errore, immagine non trovata");
e.printStackTrace();
}
background = result;
}
}
Iif i load an image on the button, it calls the repaint,and it's good, the image is shown, when i load a new Image, it calls the repaint again and the new Image is shown. The problem is when i pass the mouse over the button, it calls the rapaint, but loads the old image. Why? And how did it get the old image, as the new one should have replaced it?
I don't know why you want to go this extend and not simply use the icon property of the button, but...
First, I'd get rid of the painted flag, it runs the risk of allowing the paintComponent method to try and paint a null image, because the loadImage method may fail, but the painted flag is always set to true - Let's face it, you could easily end up with painted == true and background == null ... which is not a valid state
Instead, reason about the actual state you're interested in...
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (background != null) {
Dimension size = getSize();
g.drawImage(background, 0, 0, size.width, size.height, 0, 0, background.getWidth(), background.getHeight(), null);
}
}
I would also change the loadImage method
public void loadImage(URL url) throws IOException {
background = ImageIO.read(url);
revalidate();
repaint();
}
First, don't use String, it's meaning is to ambiguous, instead, specify the lowest common parameter you're willing to deal with.
Secondly, the image is either going to load or it's not, if you're not going to deal with the exception, in any meaningful fashion, pass it back to the caller who may be in a better position to deal with it - IMHO
I don't know if this important or not, but I'd also consider overriding the getPreferredSize method...
#Override
public Dimension getPreferredSize() {
return background == null ? new Dimension(10, 10) : new Dimension(background.getWidth(), background.getHeight());
}
This will at least try and match the button's size to the size of the image
This is a very simple problem to some people I'm assuming and I just can't see it. (I'm very amateur at Java).
This is some test code I wrote to try and troubleshoot why it's not working in my other project. For some reason my rocketshipStationary.png just won't show up when I load the Java Applet.
This is my code:
public class Test extends Applet {
public Image offScreen;
public Graphics d;
public BufferedImage rocketship;
public void init() {
setSize(854, 480);
offScreen = createImage(854,480);
d = offScreen.getGraphics();
try {
rocketship = ImageIO.read(new File("rocketshipStationary.png"));
} catch (IOException e) {
e.printStackTrace();
}
}
public void paint(Graphics g) {
d.clearRect(0, 0, 854, 480);
d.drawImage(rocketship, 100, 100, this);
d.drawImage(offScreen, 0, 0, this);
}
}
You should be getting a nice big stack trace that describes what happens. The bottom line is that 'applets and files do not play well together'.
Instead, either establish an URL to the image and use that for ImageIO, or alternately use the URL in the Applet.getImage(URL) method.
I am using Jtree for listing various images of a directory, I want to display image on applet when the user click on the image name displayed in the Tree, the code i'm using is as below, ta is an object of the applet because i'm using it in another class.
private void displayImage(URL furl, String fname) {
ta.Picture = ta.getImage(furl, fname);
prepareImage(ta.Picture, this);
Graphics g = ta.imageCanvas.getGraphics();
g.clearRect(10, 10, 800, 800);
g.drawImage(ta.Picture, 10, 10, this);
} // displayImage
public void valueChanged(TreeSelectionEvent e)
{
// TODO Auto-generated method stub
FileTreeNode node = (FileTreeNode) tree.getLastSelectedPathComponent();
System.out.println("slecte asldf " + node.isLeaf());
if (node.isLeaf())
{
currentFile = node.file;
System.out.println("File name " + currentFile.getName());
try
{
URL furl = new URL("file:/F:/photos");
displayImage(furl, currentFile.getName());
}
catch (MalformedURLException mle)
{
System.out.println("Exception::::::" + mle);
}
}
else
currentFile = null;
}
But its not working.
As you are showing files from the local filesystem, working with URLs is not required. Use
displayImage(currentFile);
and rewrite that method as following:
private void displayImage(File file) {
BufferedImage image = ImageIO.read(file);
ta.image = image;
ta.repaint();
}
where the paint method of the (I an assuming) component ta must be like
BufferedImage image;
public void paint(Graphics g) {
g.clearRect(10, 10, 800, 800);
g.drawImage(ta.Picture, 10, 10, this);
}
Because of security reasons, the applet will only be able to access the file system if signed or running without security manager (most often on the same computer).
But its not working.
This is in no way helpful, do you get exceptions? What happens? Please post an SSCCE for better help sooner
I want to display image on applet when the user click on the image
name displayed in the Tree, the code i'm using is as below, ta is an
object of the applet because i'm using it in another class.
IMO you are going about it wrong using the JPanel object and Component#getGraphics.
Dont use Component#getGraphics() as its not good practice and not persistent thus on next call to repaint() the screen will be cleared.
Dont use Applet with Swing components rather use JApplet.
Add a custom JPanel with getters and setters for BufferedImage variable to the container and than override paintComponnet and draw the BufferedImage there.
Now to change the BufferedImage simply call the setter i.e setBackgroundImage(BufferedImage img) and than call repaint() on JPanel to show the changes. Like so:
public class MyPanel extends JPanel {
private BufferedImage bg;
public MyPanel(BufferedImage bi) {
bg=bi;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d=(Graphics2D)g;
g2d.addRenderingHints(new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY));
g2d.addRenderingHints(new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON));
g2d.drawImage(bg,0,0,this);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(bg.getWidth(),bg.getHeight());
}
public BufferedImage setBackgroundImage(BufferedImage bi) {
bg=bi;
}
}
Now we use it like so:
MyPanel mp=new MyPanel(...);//create the panel with an image
...
add(mp);//add to container
...
mp.setBackgroundImage(..);//change the image being displayed
mp.repaint();//so the new image may be painted
I'm trying to add a JPanel with a picture in it. I'm using ImageIO.read to get the path but i get an IOException saying : can't read input file
The picture is called TCHLogo. It's a PNG inside a 'res' folder inside my project.
If there is any better way of displaying this image please also mention it!
Here is the code for my JPanel:
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JPanel;
public class ImagePanel extends JPanel{
private BufferedImage image;
public ImagePanel() {
try {
//THIS LINE BELLOW WAS ADDED
image = ImageIO.read(getClass().getResourceAsStream("res/TCHLogo.png"));
} catch (IOException ex) {
// handle exception...
System.out.println(ex);
}
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g); //THIS LINE WAS ADDED
g.drawImage(image, 0, 0, null); // see javadoc for more info on the parameters
}
}
Here is how i add the JPanel in my Applet:
ImagePanel appletRunningPanel;
appletRunningPanel = new ImagePanel();
appletRunningPanel.setSize(300, 300);
appletRunningPanel.validate();
add(appletRunningPanel);
EDIT
I created a folder inside the bin which the application starts to look in currently..
the folder is called res and the picture is inside..
Now i get the following IOException when i run the line:
image = ImageIO.read(getClass().getResourceAsStream("res/TCHLogo.png"));
Here is the error log:
java.lang.IllegalArgumentException: input == null!
at javax.imageio.ImageIO.read(ImageIO.java:1338)
at surprice.applet.ImagePanel.<init>(ImagePanel.java:17)
at surprice.applet.MainClass.init(MainClass.java:41)
at sun.applet.AppletPanel.run(AppletPanel.java:436)
at java.lang.Thread.run(Thread.java:679)
Likely your image's file path isn't correct relative to the user directory. To find out where Java is starting to look, where the user directory is, place something like this line of code somewhere in your program:
System.out.println(System.getProperty("user.dir"));
Perhaps you'd be better off getting the image as an InputStream obtained from a resource and not as a file. e.g.,
image = ImageIO.read(getClass().getResourceAsStream("res/TCHLogo.png"));
This will look for the image at the path given relative to the location of the class files, and in fact this is what you must do if your image is located in your jar file.
Edit 2
As an aside, often you need to first call the super's paintComponent method before doing any of your own drawing so as to allow necessary house-keeping can be done, such as getting rid of "dirty" image bits. e.g., change this:
public void paintComponent(Graphics g) {
g.drawImage(image, 0, 0, null);
}
to this:
public void paintComponent(Graphics g) {
super.paintComponent(g); // **** added****
g.drawImage(image, 0, 0, null);
}
I've written this ImagePanel class that i use for this scope :
public class ImagePanel extends JPanel {
private static final long serialVersionUID = 7952119619331504986L;
private BufferedImage image;
public ImagePanel() { }
public ImagePanel(String resName) throws IOException {
loadFromResource(resName);
}
public ImagePanel(BufferedImage image) {
this.image = image;
}
#Override
public void paintComponent(Graphics g) {
g.drawImage(image, 0, 0, null); // see javadoc for more info on the parameters
}
public BufferedImage getImage() {
return image;
}
public void setImage(BufferedImage image) {
this.image = image;
}
/**
* Load the Image from a File
* #param path image name and path
* #throws IOException
*/
public void loadFromFile(String path) throws IOException {
image = ImageIO.read(new java.io.File(path));
}
/**
* Load Image from resource item
* #param resName name of the resource (e.g. : image.png)
* #throws IOException
*/
public void loadFromResource(String resName) throws IOException {
URL url = this.getClass().getResource(resName);
BufferedImage img = ImageIO.read(url);
image = img;
}
}
Then i use the ImagePanel in this way :
//Inizialization of the ImagePanel
ImagePanel panelImage = new ImagePanel();
//Try loading image into the image panel
try {
panelImage.loadFromResource("/Resources/someimage.png");
} catch (java.io.IOException e) {
//Handling Exception
}