In trying to make an animated GIF from an image with transparency this code is producing a ghosting effect from the earlier image frames.
Image of 3rd frame, which shows the first two frames beneath the final frame
How can the code be altered to produce a 'clean' animated image for all frames?
Note: The code uses two methods, written by GeoffTitmus & Maxideon, that were formerly on the Oracle forums site. The links I first saw referenced in the code (in the JavaDocs section) have long since disappeared, so were removed. Other than that, only a few JavaDoc warnings were corrected, otherwise they are exactly as found from around the internet or as used in my older code projects.
import java.awt.*;
import java.awt.image.*;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import java.io.*;
import javax.imageio.*;
import javax.imageio.metadata.IIOInvalidTreeException;
import javax.imageio.stream.*;
import javax.imageio.metadata.*;
import org.w3c.dom.Node;
import java.net.URL;
public class AnimatedGifWithTransparency {
private JComponent ui = null;
public final String PATH = "https://i.stack.imgur.com/P59NF.png";
private BufferedImage image;
AnimatedGifWithTransparency() {
try {
initUI();
} catch (IOException ex) {
ex.printStackTrace();
}
}
/**
* #author GeoffTitmus
* #param out OutputStream A stream in which to store the animation.
* #param frames BufferedImage[] Array of BufferedImages, the frames of the
* animation.
* #param delayTimes String[] Array of Strings, representing the frame delay
* times.
* #throws Exception
*/
public static void saveAnimate(OutputStream out, BufferedImage[] frames, String[] delayTimes) throws Exception {
ImageWriter iw = ImageIO.getImageWritersByFormatName("gif").next();
ImageOutputStream ios = ImageIO.createImageOutputStream(out);
iw.setOutput(ios);
iw.prepareWriteSequence(null);
for (int i = 0; i < frames.length; i++) {
BufferedImage src = frames[i];
ImageWriteParam iwp = iw.getDefaultWriteParam();
IIOMetadata metadata = iw.getDefaultImageMetadata(new ImageTypeSpecifier(src), iwp);
configure(metadata, delayTimes[i], i);
IIOImage ii = new IIOImage(src, null, metadata);
iw.writeToSequence(ii, null);
}
iw.endWriteSequence();
ios.close();
}
/**
* #author Maxideon
* #param meta
* #param delayTime String Frame delay for this frame.
* #param imageIndex
*/
public static void configure(IIOMetadata meta,
String delayTime,
int imageIndex) {
String metaFormat = meta.getNativeMetadataFormatName();
if (!"javax_imageio_gif_image_1.0".equals(metaFormat)) {
throw new IllegalArgumentException(
"Unfamiliar gif metadata format: " + metaFormat);
}
Node root = meta.getAsTree(metaFormat);
//find the GraphicControlExtension node
Node child = root.getFirstChild();
while (child != null) {
if ("GraphicControlExtension".equals(child.getNodeName())) {
break;
}
child = child.getNextSibling();
}
IIOMetadataNode gce = (IIOMetadataNode) child;
gce.setAttribute("userDelay", "FALSE");
gce.setAttribute("delayTime", delayTime);
//only the first node needs the ApplicationExtensions node
if (imageIndex == 0) {
IIOMetadataNode aes
= new IIOMetadataNode("ApplicationExtensions");
IIOMetadataNode ae
= new IIOMetadataNode("ApplicationExtension");
ae.setAttribute("applicationID", "NETSCAPE");
ae.setAttribute("authenticationCode", "2.0");
byte[] uo = new byte[]{
//last two bytes is an unsigned short (little endian) that
//indicates the the number of times to loop.
//0 means loop forever.
0x1, 0x0, 0x0
};
ae.setUserObject(uo);
aes.appendChild(ae);
root.appendChild(aes);
}
try {
meta.setFromTree(metaFormat, root);
} catch (IIOInvalidTreeException e) {
//shouldn't happen
throw new Error(e);
}
}
private BufferedImage getShiftedImage(int frame) {
int w = image.getWidth();
int h = image.getHeight();
BufferedImage bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
Graphics g = bi.getGraphics();
int xOffPos = frame*w/3;
int xOffNeg = xOffPos-w;
g.drawImage(image, xOffNeg, 0, ui);
g.drawImage(image, xOffPos, 0, ui);
g.dispose();
return bi;
}
public final void initUI() throws IOException {
if (ui != null) {
return;
}
image = ImageIO.read(new URL(PATH));
ui = new JPanel(new BorderLayout(4, 4));
ui.setBorder(new EmptyBorder(4, 4, 4, 4));
BufferedImage[] frames = {
getShiftedImage(0),
getShiftedImage(1),
getShiftedImage(2)
};
String[] delayTimes = {
"50","50","50"
};
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
saveAnimate(baos, frames, delayTimes);
} catch (Exception ex) {
ex.printStackTrace();
return;
}
byte[] bytes = baos.toByteArray();
ui.add(new JLabel(new ImageIcon(bytes)));
}
public JComponent getUI() {
return ui;
}
public static void main(String[] args) {
Runnable r = () -> {
AnimatedGifWithTransparency o = new AnimatedGifWithTransparency();
JFrame f = new JFrame(o.getClass().getSimpleName());
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.setLocationByPlatform(true);
f.setContentPane(o.getUI());
f.pack();
f.setMinimumSize(f.getSize());
f.setVisible(true);
};
SwingUtilities.invokeLater(r);
}
}
Related
I am trying to read image objects from a PDF document. The image comes out as black background and white text. How can I reverse that. the image in the pdf, is white foreground and black background.
Here is the code, main piece for loading the image component
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import javax.imageio.ImageIO;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Image;
import com.itextpdf.text.pdf.PRStream;
import com.itextpdf.text.pdf.PdfDictionary;
import com.itextpdf.text.pdf.PdfName;
import com.itextpdf.text.pdf.parser.ImageRenderInfo;
import com.itextpdf.text.pdf.parser.PdfImageObject;
import com.itextpdf.text.pdf.parser.RenderListener;
import com.itextpdf.text.pdf.parser.TextRenderInfo;
public class MyImageRenderListener implements RenderListener {
/**
* The new document to which we've added a border rectangle.
*/
protected String path = "";
/**
* Creates a RenderListener that will look for images.
*/
public MyImageRenderListener(String path) {
this.path = path;
}
/**
* #see com.itextpdf.text.pdf.parser.RenderListener#beginTextBlock()
*/
public void beginTextBlock() {
}
/**
* #see com.itextpdf.text.pdf.parser.RenderListener#endTextBlock()
*/
public void endTextBlock() {
}
/**
* #see com.itextpdf.text.pdf.parser.RenderListener#renderImage(
*com.itextpdf.text.pdf.parser.ImageRenderInfo)
*/
public void renderImage(final ImageRenderInfo renderInfo) {
try {
String filename;
FileOutputStream os;
PdfImageObject image = renderInfo.getImage();
PdfImageObject tmp = null;
PdfName filter = (PdfName) image.get(PdfName.FILTER);
///
PdfDictionary imageDictionary = image.getDictionary();
// Try SMASK, SMASKINDATA
PRStream maskStream = (PRStream) imageDictionary.getAsStream(PdfName.SMASK);
// todo - required - black white - fix
PdfImageObject maskImage = new PdfImageObject(maskStream);
image = maskImage;
if (PdfName.DCTDECODE.equals(filter)) {
filename = String.format(path, renderInfo.getRef().getNumber(), "jpg");
os = new FileOutputStream(filename);
os.write(image.getImageAsBytes());
os.flush();
os.close();
} else if (PdfName.JPXDECODE.equals(filter)) {
filename = String.format(path, renderInfo.getRef().getNumber(), "jp2");
os = new FileOutputStream(filename);
os.write(image.getImageAsBytes());
os.flush();
os.close();
} else if (PdfName.JBIG2DECODE.equals(filter)) {
// ignore: filter not supported.
} else {
BufferedImage awtimage = renderInfo.getImage().getBufferedImage();
if (awtimage != null) {
filename = String.format(path, renderInfo.getRef().getNumber(), "png");
ImageIO.write(awtimage, "png", new FileOutputStream(filename));
}
}
try {
final String newfile = String.format(path, renderInfo.getRef().getNumber(), ".x.", "png");
BufferedImage bi = image.getBufferedImage();
BufferedImage newBi = new BufferedImage(bi.getWidth(), bi.getHeight(), BufferedImage.TYPE_USHORT_GRAY);
newBi.getGraphics().drawImage(bi, 0, 0, null);
ImageIO.write(newBi, "png", new FileOutputStream(newfile));
} catch(final Exception e) {
e.printStackTrace();;
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static Image makeBlackAndWhitePng(PdfImageObject image) throws IOException, DocumentException {
BufferedImage bi = image.getBufferedImage();
BufferedImage newBi = new BufferedImage(bi.getWidth(), bi.getHeight(), BufferedImage.TYPE_USHORT_GRAY);
newBi.getGraphics().drawImage(bi, 0, 0, null);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(newBi, "png", baos);
return Image.getInstance(baos.toByteArray());
}
/**
* #see com.itextpdf.text.pdf.parser.RenderListener#renderText(
*com.itextpdf.text.pdf.parser.TextRenderInfo)
*/
public void renderText(TextRenderInfo renderInfo) {
}
}
And code around loading the pages
public static void readImages(final PdfReader reader, final File filex) throws IOException {
for (int i = 0; i < reader.getXrefSize(); i++) {
PdfObject pdfobj = reader.getPdfObject(i);
if (pdfobj == null || !pdfobj.isStream()) {
continue;
}
PdfStream stream = (PdfStream) pdfobj;
PdfObject pdfsubtype = stream.get(PdfName.SUBTYPE);
if (pdfsubtype != null && pdfsubtype.toString().equals(PdfName.IMAGE.toString())) {
byte[] img = PdfReader.getStreamBytesRaw((PRStream) stream);
FileOutputStream out = new FileOutputStream(new File(filex.getParentFile(), String.format("%1$05d", i) + ".jpg"));
out.write(img);
out.flush();
out.close();
}
}
}
To combine my comments:
What you see, merely is the soft mask of the image which contains transparency information, white = opaque, black = transparent. The base image actually is all black but only where the mask indicates opaque, that image black is drawn. Thus it looks like reversed.
You can use image manipulation libraries to invert a bitmap. Simply googl'ing for "imageio invert image" returns this, this, and numerous other interesting matches.
I capture screenshots using the BufferedImage but when I get the picture it showed in a bad quality.
Here's my code:
Rectangle screenShot=new Rectangle(graphPanel.getX()+this.getX()+5,
graphPanel.getY()+this.getY()+navPanel.getHeight()+4,
graphPanel.getWidth(), graphPanel.getHeight());
BufferedImage capture=new Robot().createScreenCapture(screenShot);
ImageIO.write(capture, "jpg",new File("./PDFs/test"+"Graph"+".png"));
ImageIO.write(capture, "jpg",new File("./PDFs/test"+"Graph"+".png"));
Should be:
ImageIO.write(capture, "png",new File("./PDFs/test"+"Graph"+".png"));
Calling an image a PNG in the file extension won't make it a PNG. It also needs to be encoded as a PNG.
[I] did [change] it, but no result
What output do you get for this code? Here I get:
type: jpg Color difference: 926283 or 0.7852198282877604%.
type: png Color difference: 0 or 0.0%.
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.imageio.ImageIO;
import java.io.*;
import java.net.*;
public class ImageSave {
private JComponent ui = null;
public final static String[] types = {"jpg","png"};
private final JComboBox<String> typesBox = new JComboBox<>(types);
BufferedImage image;
BufferedImage imageCoded;
JLabel imageLabel = new JLabel();
JLabel imageCodedLabel = new JLabel();
JLabel output = new JLabel("Output appears here..");
ImageSave() {
try {
initUI();
} catch (Exception ex) {
ex.printStackTrace();
}
}
public final void initUI() throws Exception {
if (ui != null) {
return;
}
ui = new JPanel(new BorderLayout(4,4));
ui.setBorder(new EmptyBorder(4, 4, 4, 4));
URL url = new URL("https://i.stack.imgur.com/7bI1Y.jpg");
image = ImageIO.read(url);
imageLabel.setIcon(new ImageIcon(image));
JPanel picsPanel = new JPanel(new GridLayout(1, 0, 4, 4));
ui.add(picsPanel);
picsPanel.add(imageLabel);
picsPanel.add(imageCodedLabel);
JToolBar toolBar = new JToolBar();
ui.add(toolBar, BorderLayout.PAGE_START);
toolBar.setLayout(new FlowLayout(FlowLayout.LEADING));
toolBar.add(typesBox);
ActionListener refreshListener = (ActionEvent e) -> {
try {
updateGUI();
} catch (Exception ex) {
ex.printStackTrace();
}
};
typesBox.addActionListener(refreshListener);
toolBar.add(output);
updateGUI();
}
private void updateGUI() throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
String type = typesBox.getSelectedItem().toString();
ImageIO.write(image, type, baos);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
imageCoded = ImageIO.read(bais);
imageCodedLabel.setIcon(new ImageIcon(imageCoded));
int diff = compareImages();
int max = image.getWidth()*image.getHeight()*256*3;
double percent = 100d*(double)diff/(double)max;
String s = "type: " + type + " Color difference: " + diff +
" or " + percent + "%.";
System.out.println(s);
output.setText(s);
}
private int compareImages() {
int diff = 0;
for (int xx=0; xx<image.getWidth(); xx++) {
for (int yy=0; yy<image.getHeight(); yy++) {
Color rgb1 = new Color(image.getRGB(xx, yy));
Color rgb2 = new Color(imageCoded.getRGB(xx, yy));
int r1 = rgb1.getRed();
int g1 = rgb1.getGreen();
int b1 = rgb1.getBlue();
int r2 = rgb2.getRed();
int g2 = rgb2.getGreen();
int b2 = rgb2.getBlue();
diff += Math.abs(r1-r2);
diff += Math.abs(g1-g2);
diff += Math.abs(b1-b2);
}
}
return diff;
}
public JComponent getUI() {
return ui;
}
public static void main(String[] args) {
Runnable r = () -> {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception useDefault) {
}
ImageSave o = new ImageSave();
JFrame f = new JFrame(o.getClass().getSimpleName());
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.setLocationByPlatform(true);
f.setContentPane(o.getUI());
f.pack();
f.setMinimumSize(f.getSize());
f.setVisible(true);
};
SwingUtilities.invokeLater(r);
}
}
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
The code is being modified a bit . I added a button to the Jpanel whose action event save the image on the disk .
Source Code
package ImageResize;
import java.awt.image.BufferedImage;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.RenderedImage;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.*;
import javax.swing.event.*;
import javax.imageio.*;
import java.io.*;
import javax.imageio.stream.ImageOutputStream;
import javax.imageio.plugins.jpeg.JPEGImageWriteParam;
import java.util.Locale;
import javax.swing.filechooser.FileNameExtensionFilter;
class ImageCompressionDemo {
private BufferedImage originalImage;
private BufferedImage textImage;
private JPanel gui;
private JCheckBox antialiasing;
private
JCheckBox rendering;
private JCheckBox fractionalMetrics;
private JCheckBox strokeControl;
private JCheckBox colorRendering;
private JCheckBox dithering;
private JComboBox textAntialiasing;
private JComboBox textLcdContrast;
private JLabel jpegLabel;
private JLabel pngLabel;
private JTextArea output;
private JSlider quality;
private JButton saveButton;
private int pngSize;
private int jpgSize;
final static Object[] VALUES_TEXT_ANTIALIASING = {
RenderingHints.VALUE_TEXT_ANTIALIAS_OFF,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON,
RenderingHints.VALUE_TEXT_ANTIALIAS_GASP,
RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HBGR,
RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB,
RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_VBGR,
RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_VRGB
};
final static Object[] VALUES_TEXT_LCD_CONTRAST = {
new Integer(100),
new Integer(150),
new Integer(200),
new Integer(250)
};
ImageCompressionDemo(BufferedImage inputImage) {
int width = 280;
int height = 100;
gui = new JPanel(new BorderLayout(3,4));
gui.setPreferredSize(new Dimension(400, 600));
quality = new JSlider(JSlider.VERTICAL, 0, 100, 50);
quality.setSnapToTicks(true);
quality.setPaintTicks(true);
quality.setPaintLabels(true);
quality.setMajorTickSpacing(10);
quality.setMinorTickSpacing(1);
quality.addChangeListener( new ChangeListener(){
public void stateChanged(ChangeEvent ce) {
updateImages();
}
} );
gui.add(quality, BorderLayout.WEST);
originalImage = inputImage;
textImage =inputImage;
JPanel controls = new JPanel(new GridLayout(0,1,0,0));
antialiasing = new JCheckBox("Anti-aliasing", false);
rendering = new JCheckBox("Rendering - Quality", true);
fractionalMetrics = new JCheckBox("Fractional Metrics", true);
strokeControl = new JCheckBox("Stroke Control - Pure", false);
colorRendering = new JCheckBox("Color Rendering - Quality", true);
dithering = new JCheckBox("Dithering", false);
saveButton =new JButton("Save Image");
saveButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
try {
BufferedImage img = (BufferedImage)getJpegCompressedImage(originalImage);
File outputfile = new File("C:/Users/uday/Desktop/newImage.jpg");
ImageIO.write(img, "jpg", outputfile);
} catch (IOException ex) {
Logger.getLogger(ImageCompressionDemo.class.getName()).log(Level.SEVERE, null, ex);
}
} });
controls.add(antialiasing);
controls.add(fractionalMetrics);
textLcdContrast = new JComboBox(VALUES_TEXT_LCD_CONTRAST);
JPanel lcdContrastPanel = new JPanel(new FlowLayout(FlowLayout.LEADING));
lcdContrastPanel.add(textLcdContrast);
lcdContrastPanel.add(new JLabel("Text LCD Contrast"));
controls.add(lcdContrastPanel);
textAntialiasing = new JComboBox(VALUES_TEXT_ANTIALIASING);
controls.add(textAntialiasing);
controls.add(dithering);
controls.add(rendering);
controls.add(colorRendering);
controls.add(strokeControl);
ItemListener itemListener = new ItemListener(){
public void itemStateChanged(ItemEvent e) {
updateImages();
}
};
antialiasing.addItemListener(itemListener);
rendering.addItemListener(itemListener);
fractionalMetrics.addItemListener(itemListener);
strokeControl.addItemListener(itemListener);
colorRendering.addItemListener(itemListener);
dithering.addItemListener(itemListener);
textAntialiasing.addItemListener(itemListener);
textLcdContrast.addItemListener(itemListener);
Graphics2D g2d = originalImage.createGraphics();
gui.add(controls, BorderLayout.EAST);
gui.add(saveButton,BorderLayout.BEFORE_FIRST_LINE);
JPanel images = new JPanel(new GridLayout(0,1,2,2));
images.add(new JLabel(new ImageIcon(textImage)));
try {
pngLabel = new JLabel(new ImageIcon(getPngCompressedImage(textImage)));
images.add(pngLabel);
jpegLabel = new JLabel(new ImageIcon(getJpegCompressedImage(textImage)));
images.add(jpegLabel);
} catch(IOException ioe) {
}
gui.add(images, BorderLayout.CENTER);
output = new JTextArea(4,40);
output.setEditable(false);
gui.add(new JScrollPane(output), BorderLayout.SOUTH);
updateImages();
JOptionPane.showMessageDialog(null, gui);
}
private Image getPngCompressedImage(BufferedImage image) throws IOException {
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
ImageIO.write( image, "png", outStream );
pngSize = outStream.toByteArray().length;
BufferedImage compressedImage =
ImageIO.read(new ByteArrayInputStream(outStream.toByteArray()));
return compressedImage;
}
private Image getJpegCompressedImage(BufferedImage image) throws IOException {
float qualityFloat = (float)quality.getValue()/100f;
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
ImageWriter imgWriter = ImageIO.getImageWritersByFormatName( "jpg" ).next();
ImageOutputStream ioStream = ImageIO.createImageOutputStream( outStream );
imgWriter.setOutput( ioStream );
JPEGImageWriteParam jpegParams = new JPEGImageWriteParam( Locale.getDefault() );
jpegParams.setCompressionMode( ImageWriteParam.MODE_EXPLICIT );
jpegParams.setCompressionQuality( qualityFloat );
imgWriter.write( null, new IIOImage( image, null, null ), jpegParams );
ioStream.flush();
ioStream.close();
imgWriter.dispose();
jpgSize = outStream.toByteArray().length;
BufferedImage compressedImage = ImageIO.read(new ByteArrayInputStream(outStream.toByteArray()));
return compressedImage;
}
private void updateText() {
StringBuilder builder = new StringBuilder();
builder.append("Fractional Metrics: \t");
builder.append( fractionalMetrics.isSelected() );
builder.append("\n");
builder.append( textAntialiasing.getSelectedItem() );
builder.append("\nPNG size: \t");
builder.append(pngSize);
builder.append(" bytes\n");
builder.append("JPG size: \t");
builder.append(jpgSize);
builder.append(" bytes \tquality: ");
builder.append(quality.getValue());
output.setText(builder.toString());
}
private void updateImages() {
int width = originalImage.getWidth();
int height = originalImage.getHeight();
Graphics2D g2dText = textImage.createGraphics();
if (antialiasing.isSelected()) {
g2dText.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
} else {
g2dText.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_OFF);
}
if (rendering.isSelected()) {
g2dText.setRenderingHint(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
} else {
g2dText.setRenderingHint(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_SPEED);
}
if (fractionalMetrics.isSelected()) {
g2dText.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
RenderingHints.VALUE_FRACTIONALMETRICS_ON);
} else {
g2dText.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
RenderingHints.VALUE_FRACTIONALMETRICS_OFF);
}
if (strokeControl.isSelected()) {
g2dText.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
RenderingHints.VALUE_STROKE_NORMALIZE);
} else {
g2dText.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
RenderingHints.VALUE_STROKE_PURE);
}
if (dithering.isSelected()) {
g2dText.setRenderingHint(RenderingHints.KEY_DITHERING,
RenderingHints.VALUE_DITHER_ENABLE);
} else {
g2dText.setRenderingHint(RenderingHints.KEY_DITHERING,
RenderingHints.VALUE_DITHER_DISABLE);
}
if (colorRendering.isSelected()) {
g2dText.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING,
RenderingHints.VALUE_COLOR_RENDER_QUALITY);
} else {
g2dText.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING,
RenderingHints.VALUE_COLOR_RENDER_SPEED);
}
g2dText.setRenderingHint(RenderingHints.KEY_TEXT_LCD_CONTRAST,
textLcdContrast.getSelectedItem());
g2dText.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
textAntialiasing.getSelectedItem());
g2dText.drawImage(originalImage, 0,0, null);
try {
jpegLabel.setIcon(new ImageIcon(getJpegCompressedImage(textImage)));
pngLabel.setIcon(new ImageIcon(getPngCompressedImage(textImage)));
} catch(IOException ioe) {
}
gui.repaint();
updateText();
}
public static void main(String[] args) {
SwingUtilities.invokeLater( new Runnable() {
public void run() {
try {
FileNameExtensionFilter filter = new FileNameExtensionFilter("Pictures","jpg","png");
JFileChooser fc = new JFileChooser();
fc.setFileFilter(filter);
int returnVal = fc.showOpenDialog(null);
if (returnVal == JFileChooser.APPROVE_OPTION) {
File file = fc.getSelectedFile();
String Path = file.getPath();
BufferedImage originalImage = ImageIO.read(new File(Path));
ImageCompressionDemo iwt = new ImageCompressionDemo(originalImage);
}
} catch (IOException ex) {
Logger.getLogger(ImageCompressionDemo.class.getName()).log(Level.SEVERE, null, ex);
}
}
} );
}
}
Here the Image size displayed in jpegLabel label variable is different from what is being saved on the disk . I want the size of the image saved on the disk to be of same size which is being displayed .
Reference to the Question : Need Java function that takes image and imagesize(in Kb) as input and return an image
The problem is here:
BufferedImage img = (BufferedImage)getJpegCompressedImage(originalImage);
File outputfile = new File("C:/Users/uday/Desktop/newImage.jpg");
ImageIO.write(img, "jpg", outputfile);
..when using the ImageIO class that way, it ignores whatever compression has been applied to the image and saves it using standard compression! The trick to saving the compressed image is to change the getJpegCompressedImage method to saveJpegCompressedImage, and instead of returning an Image it should declare void and save the byte[] directly to disk (using standard I/O).
E.G. as seen in this example:
Original image (unknown compression)
As seen in Example images for code and mark-up Q&As.
Saved as quality .2 (6,327 bytes)
Saved as quality .8 (18,702 bytes)
Code
import java.awt.Desktop;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.URL;
import java.util.Locale;
import javax.imageio.*;
import javax.imageio.plugins.jpeg.JPEGImageWriteParam;
import javax.imageio.stream.ImageOutputStream;
public class CompressImage {
/** TODO: This method has terrible handling of I/O.
Rewrite for production use. BNI. */
static public void saveJpegCompressedImage(
BufferedImage image,
float quality,
File file) throws Exception {
OutputStream outStream = new FileOutputStream(
new File(file, "Image-" + quality + ".jpg"));
ImageWriter imgWriter = ImageIO.
getImageWritersByFormatName("jpg").next();
ImageOutputStream ioStream =
ImageIO.createImageOutputStream(outStream);
imgWriter.setOutput(ioStream);
JPEGImageWriteParam jpegParams = new JPEGImageWriteParam(
Locale.getDefault());
jpegParams.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
jpegParams.setCompressionQuality(quality);
imgWriter.write(null, new IIOImage(image, null, null), jpegParams);
ioStream.flush();
ioStream.close();
imgWriter.dispose();
}
public static void main(String[] args) throws Exception {
URL url = new URL("http://i.stack.imgur.com/7bI1Y.jpg");
File f = new File(System.getProperty("user.home"));
BufferedImage bi = ImageIO.read(url);
for (float q = 0.2f; q < .9f; q += .2f) {
saveJpegCompressedImage(bi, q, f);
}
Desktop.getDesktop().open(f);
}
}
Ok, i compile the code and run the program the image appears as it should in the center of the screen. I put the dimensions in the JTextFields and press the resize button. Everything works fine. Now if i close the window and run again the image appears but if i press the resize button the first time it dissapears, if i press it again it appears and everything works fine.
Only if i compile the code while the window is running and run i don;t have that minor issue.
Anyone has any clues of what is hapening?
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
public class ImageApp extends JFrame implements ActionListener{
//*********************************Variables*********************************
//***************************************************************************
private JButton align_l,align_c,align_r,rsz;
private JLabel w,h,pic;
private JTextField w_txt,h_txt;
private static final String IMG_PATH = "./hamster.jpg";
private static int img_w,img_h;
private static ImageIcon icon;
private JMenuItem reset_option,double_option;
//********************************Constructor********************************
//***************************************************************************
public ImageApp(String title){
super(title);
//****************************Align Panel****************************
align_l = new JButton("Align Left");
align_c = new JButton("Align Center");
align_r = new JButton("Align Right");
align_l.addActionListener(this);
align_c.addActionListener(this);
align_r.addActionListener(this);
JPanel align_panel = new JPanel();
align_panel.setLayout(new GridLayout(1,3));
align_panel.add(align_l);
align_panel.add(align_c);
align_panel.add(align_r);
add("North",align_panel);
//***************************Picture Label***************************
try {
BufferedImage img = ImageIO.read(new File(IMG_PATH));
img_w = img.getWidth();
img_h = img.getHeight();
ImageIcon icon = new ImageIcon(img);
pic = new JLabel(icon);
add(pic);
} catch (IOException e) {
e.printStackTrace();
}
//****************************Resize Panel***************************
w = new JLabel("Width:");
h = new JLabel("Height:");
w_txt = new JTextField(String.valueOf(img_w),4);
h_txt = new JTextField(String.valueOf(img_h),4);
rsz = new JButton("Resize");
rsz.addActionListener(this);
JPanel resize_panel = new JPanel();
resize_panel.setLayout(new GridLayout(3,2));
resize_panel.add(w);
resize_panel.add(w_txt);
resize_panel.add(h);
resize_panel.add(h_txt);
resize_panel.add(rsz);
add("South",resize_panel);
//*************************Menu Options Panel************************
JMenuBar menu_bar = new JMenuBar();
setJMenuBar(menu_bar);
JMenu options_menu = new JMenu("Options");
menu_bar.add(options_menu);
JMenuItem reset_option = new JMenuItem("Reset");
JMenuItem double_option = new JMenuItem("Double");
options_menu.add(reset_option);
options_menu.add(double_option);
reset_option.addActionListener(this);
double_option.addActionListener(this);
//********************************************************************
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent evt) {
System.exit(0);
}
});
}
//**********************************Methods**********************************
//***************************************************************************
public void actionPerformed(ActionEvent e){
//Object b = e.getSource();
String b = e.getActionCommand();
if (b.equals("Align Left")){pic.setHorizontalAlignment(SwingConstants.LEFT);}
else if (b.equals("Align Center")){pic.setHorizontalAlignment(SwingConstants.CENTER);}
else if (b.equals("Align Right")){pic.setHorizontalAlignment(SwingConstants.RIGHT);}
else if (b.equals("Resize")){
int wid,hei;
try{
wid=Integer.parseInt(w_txt.getText());
hei=Integer.parseInt(h_txt.getText());
}catch(NumberFormatException nfe){
wid=img_w;
hei=img_h;
}
if(wid == 0 || hei == 0){
wid=img_w;
hei=img_h;
}
if(wid > 4096){
wid=4096;
}
if( hei > 2048){
hei=2048;
}
resize_image(wid,hei);
}
else if (b.equals("Reset")){
resize_image(img_w,img_h);
}
else if (b.equals("Double")){
Icon newIcon = pic.getIcon();
int wid = 2 * newIcon.getIconWidth();
int hei = 2 * newIcon.getIconHeight();
if(wid > 4096){
wid=4096;
}
if( hei > 2048){
hei=2048;
}
resize_image(wid,hei);
}
}
public void resize_image(int w,int h){
Image im = Toolkit.getDefaultToolkit().getImage(IMG_PATH);
BufferedImage b_i = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
Graphics g = b_i.createGraphics();
g.drawImage(im, 0, 0, w, h, null);
ImageIcon newIcon = new ImageIcon(b_i);
pic.setIcon(newIcon);
w_txt.setText(String.valueOf(w));
h_txt.setText(String.valueOf(h));
}
public static void main(String [] args){
ImageApp i = new ImageApp("Image Application.");
i.setSize(800,600);
i.setLocationRelativeTo(null);
i.setVisible(true);
}
}
In resize_image:
public void resize_image(int w, int h) {
Image im = Toolkit.getDefaultToolkit().getImage(IMG_PATH);
/* ... */
g.drawImage(im, 0, 0, w, h, null);
/* ... */
}
At the first time getImage is called, im wasn't really loaded. When drawImage is called, AWT started to load the image in another thread. It didn't block until the image is loaded. And, because drawImage thinks that the image wasn't complete loaded, it didn't to anything.
At the second time getImage is called, im has been loaded because AWT will automatically cache the image, so everything work fine.
Solution: MediaTracker
public void resize_image(int w, int h) {
Image im = Toolkit.getDefaultToolkit().getImage(IMG_PATH);
waitForImage(im);
/* ... */
g.drawImage(im, 0, 0, w, h, null);
/* ... */
}
private MediaTracker tracker = new MediaTracker(this);
private void waitForImage(Image image){
tracker.addImage(image, 0);
try {
tracker.waitForID(0);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import javax.imageio.ImageIO;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import common.ResourcesToAccess;
public class RecordingStartingThread extends Thread{
#Override
public void run(){
JFrame f = new JFrame();
ImageIcon reel = new ImageIcon("src/images/reel.GIF");
JLabel label = new JLabel(reel);
reel.setImageObserver(label);
f.getContentPane().add(label);
f.setUndecorated(true);
f.setSize(300, 300);
f.setVisible(true);
}
public static void main(String[] args) {
new RecordingStartingThread().start();
}
}
Issue: GIF plays extremely fast.
Question: How do I make sure that GIF plays at a normal speed?
As for GIF speed playback - I've encountered this problem too. If I remember correctly this was caused by the "default" (or not provided?) value for frame rate in GIF file. Some web browsers "overrided" that frame rate so that GIF played correctly.
As a result I created a class that converts GIF (read GIF -> write GIF) and give frame rate provided by the user. com.madgag.gif.fmsware.AnimatedGifEncoder class is an external library that I link to the project via Maven: animated-gif-lib-1.0.jar
public final class GIFUtils {
private GIFUtils() {
}
public static List<BufferedImage> extractFrames(String filePath) throws IOException {
return extractFrames(new File(filePath));
}
public static List<BufferedImage> extractFrames(File file) throws IOException {
List<BufferedImage> imgs = new LinkedList<BufferedImage>();
ImageReader reader = ImageIO.getImageReadersBySuffix("GIF").next();
ImageInputStream in = null;
try {
in = ImageIO.createImageInputStream(new FileInputStream(file));
reader.setInput(in);
BufferedImage img = null;
int count = reader.getNumImages(true);
for(int i = 0; i < count; i++) {
Node tree = reader.getImageMetadata(i).getAsTree("javax_imageio_gif_image_1.0");
int x = Integer.valueOf(tree.getChildNodes().item(0).getAttributes()
.getNamedItem("imageLeftPosition").getNodeValue());
int y = Integer.valueOf(tree.getChildNodes().item(0).getAttributes()
.getNamedItem("imageTopPosition").getNodeValue());
BufferedImage image = reader.read(i);
if(img == null) {
img = new BufferedImage(image.getWidth() + x, image.getHeight() + y,
BufferedImage.TYPE_4BYTE_ABGR);
}
Graphics2D g = img.createGraphics();
ImageUtils.setBestRenderHints(g);
g.drawImage(image, x, y, null);
imgs.add(ImageUtils.copy(img));
}
}
finally {
if(in != null) {
in.close();
}
}
return imgs;
}
public static void writeGif(List<BufferedImage> images, File gifFile, int millisForFrame)
throws FileNotFoundException, IOException {
BufferedImage firstImage = images.get(0);
int type = firstImage.getType();
ImageOutputStream output = new FileImageOutputStream(gifFile);
// create a gif sequence with the type of the first image, 1 second
// between frames, which loops continuously
GifSequenceWriter writer = new GifSequenceWriter(output, type, 100, false);
// write out the first image to our sequence...
writer.writeToSequence(firstImage);
for(int i = 1; i < images.size(); i++) {
BufferedImage nextImage = images.get(i);
writer.writeToSequence(nextImage);
}
writer.close();
output.close();
}
public static Image createGif(List<BufferedImage> images, int millisForFrame) {
AnimatedGifEncoder g = new AnimatedGifEncoder();
ByteArrayOutputStream out = new ByteArrayOutputStream(5 * 1024 * 1024);
g.start(out);
g.setDelay(millisForFrame);
g.setRepeat(1);
for(BufferedImage i : images) {
g.addFrame(i);
}
g.finish();
byte[] bytes = out.toByteArray();
return Toolkit.getDefaultToolkit().createImage(bytes);
}
And GifSequenceWriter looks like this:
public class GifSequenceWriter {
protected ImageWriter gifWriter;
protected ImageWriteParam imageWriteParam;
protected IIOMetadata imageMetaData;
public GifSequenceWriter(ImageOutputStream outputStream, int imageType, int timeBetweenFramesMS,
boolean loopContinuously) throws IIOException, IOException {
gifWriter = getWriter();
imageWriteParam = gifWriter.getDefaultWriteParam();
ImageTypeSpecifier imageTypeSpecifier = ImageTypeSpecifier.createFromBufferedImageType(imageType);
imageMetaData = gifWriter.getDefaultImageMetadata(imageTypeSpecifier, imageWriteParam);
String metaFormatName = imageMetaData.getNativeMetadataFormatName();
IIOMetadataNode root = (IIOMetadataNode) imageMetaData.getAsTree(metaFormatName);
IIOMetadataNode graphicsControlExtensionNode = getNode(root, "GraphicControlExtension");
graphicsControlExtensionNode.setAttribute("disposalMethod", "none");
graphicsControlExtensionNode.setAttribute("userInputFlag", "FALSE");
graphicsControlExtensionNode.setAttribute("transparentColorFlag", "FALSE");
graphicsControlExtensionNode.setAttribute("delayTime", Integer.toString(timeBetweenFramesMS / 10));
graphicsControlExtensionNode.setAttribute("transparentColorIndex", "0");
IIOMetadataNode commentsNode = getNode(root, "CommentExtensions");
commentsNode.setAttribute("CommentExtension", "Created by MAH");
IIOMetadataNode appEntensionsNode = getNode(root, "ApplicationExtensions");
IIOMetadataNode child = new IIOMetadataNode("ApplicationExtension");
child.setAttribute("applicationID", "NETSCAPE");
child.setAttribute("authenticationCode", "2.0");
int loop = loopContinuously ? 0 : 1;
child.setUserObject(new byte[] { 0x1, (byte) (loop & 0xFF), (byte) (loop >> 8 & 0xFF) });
appEntensionsNode.appendChild(child);
imageMetaData.setFromTree(metaFormatName, root);
gifWriter.setOutput(outputStream);
gifWriter.prepareWriteSequence(null);
}
public void writeToSequence(RenderedImage img) throws IOException {
gifWriter.writeToSequence(new IIOImage(img, null, imageMetaData), imageWriteParam);
}
public void close() throws IOException {
gifWriter.endWriteSequence();
}
private static ImageWriter getWriter() throws IIOException {
Iterator<ImageWriter> iter = ImageIO.getImageWritersBySuffix("gif");
if(!iter.hasNext()) {
throw new IIOException("No GIF Image Writers Exist");
}
return iter.next();
}
private static IIOMetadataNode getNode(IIOMetadataNode rootNode, String nodeName) {
int nNodes = rootNode.getLength();
for(int i = 0; i < nNodes; i++) {
if(rootNode.item(i).getNodeName().compareToIgnoreCase(nodeName) == 0) {
return (IIOMetadataNode) rootNode.item(i);
}
}
IIOMetadataNode node = new IIOMetadataNode(nodeName);
rootNode.appendChild(node);
return node;
}
}
The easiest fix for this problem is to just create a .gif file which is "well-formed", i.e. contains the appropriate frame rate. This can be achieved with the help of various online converters, just google "gif speed changer".