Java: Animated GIFs go automatically partially transparent - java

I would like to display a wait dialog with an animated GIF using Java Swing, but the GIFs are displayed incorrectly.
Code Example:
final JDialog progressDialog = new JDialog(new Frame(), "...", true);
URL url = getClass().getResource("wait.gif");
Icon icon = new ImageIcon(url);
JLabel label = new JLabel(icon);
progressDialog.getContentPane().setBackground(new Color (0, 200, 200));
progressDialog.getContentPane().add(label);
progressDialog.pack();
progressDialog.setVisible(true);
Original GIF is here:
But the Java output looks like this:
Any ideas what could be wrong?

When ever you have these types of problems, you want to start playing around with the disposalMethod of the frames.
I ran your gif through some inspection code and found the disposalMethod to be set to RESTORE_TO_BACKGROUND
So, basically, I took your gif and ran it through the following code, which created a new gif with the disposalMethod of none
So your original image is on top and the "fixed" image is on the bottom
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.imageio.IIOException;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataNode;
import javax.imageio.stream.FileImageOutputStream;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageOutputStream;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
public class MirrorImage {
public static void main(String[] args) {
new MirrorImage();
}
public MirrorImage() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private ImageIcon orig;
private ImageIcon mirror;
public TestPane() {
mirror(new File("Qzlxj.gif"), new File("Test.gif"));
orig = new ImageIcon("Qzlxj.gif");
mirror = new ImageIcon("Test.gif");
}
#Override
public Dimension getPreferredSize() {
return mirror == null ? new Dimension(200, 200) : new Dimension(orig.getIconWidth(), orig.getIconHeight() * 2);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (orig != null) {
Graphics2D g2d = (Graphics2D) g.create();
int x = (getWidth() - orig.getIconWidth()) / 2;
int y = (getHeight() - (orig.getIconHeight() * 2)) / 2;
g2d.drawImage(orig.getImage(), x, y, this);
// AffineTransform at = new AffineTransform();
// at.setToScale(1, -1);
// at.translate(0, -mirror.getIconHeight());
// g2d.setTransform(at);
g2d.drawImage(mirror.getImage(), x, y + mirror.getIconHeight(), this);
g2d.dispose();
}
}
}
public static void mirror(File source, File dest) {
List<BufferedImage> images = new ArrayList<>(25);
List<Integer> delays = new ArrayList<>(25);
int delay = 0;
ImageOutputStream output = null;
GifSequenceWriter writer = null;
try {
String[] imageatt = new String[]{
"imageLeftPosition",
"imageTopPosition",
"imageWidth",
"imageHeight"
};
ImageReader reader = (ImageReader) ImageIO.getImageReadersByFormatName("gif").next();
ImageInputStream ciis = ImageIO.createImageInputStream(source);
reader.setInput(ciis, false);
int noi = reader.getNumImages(true);
BufferedImage master = null;
for (int i = 0; i < noi; i++) {
BufferedImage image = reader.read(i);
IIOMetadata metadata = reader.getImageMetadata(i);
Node tree = metadata.getAsTree("javax_imageio_gif_image_1.0");
NodeList children = tree.getChildNodes();
for (int j = 0; j < children.getLength(); j++) {
Node nodeItem = children.item(j);
System.out.println(nodeItem.getNodeName());
if (nodeItem.getNodeName().equals("ImageDescriptor")) {
Map<String, Integer> imageAttr = new HashMap<String, Integer>();
NamedNodeMap attr = nodeItem.getAttributes();
// for (int index = 0; index < attr.getLength(); index++) {
// Node node = attr.item(index);
// System.out.println("----> " + node.getNodeName() + "=" + node.getNodeValue());
// }
for (int k = 0; k < imageatt.length; k++) {
Node attnode = attr.getNamedItem(imageatt[k]);
imageAttr.put(imageatt[k], Integer.valueOf(attnode.getNodeValue()));
}
if (master == null) {
master = new BufferedImage(imageAttr.get("imageWidth"), imageAttr.get("imageHeight"), BufferedImage.TYPE_INT_ARGB);
}
Graphics2D g2d = master.createGraphics();
g2d.drawImage(image, imageAttr.get("imageLeftPosition"), imageAttr.get("imageTopPosition"), null);
g2d.dispose();
// BufferedImage frame = mirror(copyImage(master));
BufferedImage frame = copyImage(master);
ImageIO.write(frame, "png", new File("img" + i + ".png"));
images.add(frame);
} else if (nodeItem.getNodeName().equals("GraphicControlExtension")) {
NamedNodeMap attr = nodeItem.getAttributes();
Node delayNode = attr.getNamedItem("delayTime");
if (delayNode != null) {
delay = Math.max(delay, Integer.valueOf(delayNode.getNodeValue()));
delays.add(delay);
}
}
}
}
output = new FileImageOutputStream(dest);
writer = new GifSequenceWriter(output, images.get(0).getType(), delay * 10, true);
for (int i = 0; i < images.size(); i++) {
BufferedImage nextImage = images.get(i);
writer.writeToSequence(nextImage);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
writer.close();
} catch (Exception e) {
}
try {
output.close();
} catch (Exception e) {
}
}
}
public static BufferedImage mirror(BufferedImage img) {
BufferedImage mirror = createCompatibleImage(img);
Graphics2D g2d = mirror.createGraphics();
AffineTransform at = new AffineTransform();
at.setToScale(1, -1);
at.translate(0, -img.getHeight());
g2d.setTransform(at);
g2d.drawImage(img, 0, 0, null);
g2d.dispose();
return mirror;
}
public static BufferedImage copyImage(BufferedImage img) {
int width = img.getWidth();
int height = img.getHeight();
BufferedImage newImage = createCompatibleImage(img);
Graphics graphics = newImage.createGraphics();
int x = (width - img.getWidth()) / 2;
int y = (height - img.getHeight()) / 2;
graphics.drawImage(img, x, y, img.getWidth(), img.getHeight(), null);
graphics.dispose();
return newImage;
}
public static BufferedImage createCompatibleImage(BufferedImage image) {
return getGraphicsConfiguration().createCompatibleImage(image.getWidth(), image.getHeight(), image.getTransparency());
}
public static GraphicsConfiguration getGraphicsConfiguration() {
return GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
}
public static class GifSequenceWriter {
protected ImageWriter gifWriter;
protected ImageWriteParam imageWriteParam;
protected IIOMetadata imageMetaData;
/**
* Creates a new GifSequenceWriter
*
* #param outputStream the ImageOutputStream to be written to
* #param imageType one of the imageTypes specified in BufferedImage
* #param timeBetweenFramesMS the time between frames in miliseconds
* #param loopContinuously wether the gif should loop repeatedly
* #throws IIOException if no gif ImageWriters are found
*
* #author Elliot Kroo (elliot[at]kroo[dot]net)
*/
public GifSequenceWriter(
ImageOutputStream outputStream,
int imageType,
int timeBetweenFramesMS,
boolean loopContinuously) throws IIOException, IOException {
// my method to create a writer
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");
//restoreToBackgroundColor
//restoreToPrevious
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);
}
/**
* Close this GifSequenceWriter object. This does not close the
* underlying stream, just finishes off the GIF.
*/
public void close() throws IOException {
gifWriter.endWriteSequence();
}
/**
* Returns the first available GIF ImageWriter using
* ImageIO.getImageWritersBySuffix("gif").
*
* #return a GIF ImageWriter object
* #throws IIOException if no GIF image writers are returned
*/
private static ImageWriter getWriter() throws IIOException {
Iterator<ImageWriter> iter = ImageIO.getImageWritersBySuffix("gif");
if (!iter.hasNext()) {
throw new IIOException("No GIF Image Writers Exist");
} else {
return iter.next();
}
}
/**
* Returns an existing child node, or creates and returns a new child
* node (if the requested node does not exist).
*
* #param rootNode the <tt>IIOMetadataNode</tt> to search for the child
* node.
* #param nodeName the name of the child node.
*
* #return the child node, if found or a new node created with the given
* name.
*/
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);
}
}
}
And finally, the "fixed" gif
The above is based on the investigations from Mirroring animated gif on load in Java - ImageIcon

Related

rotate image in grid in Java

Let's say I have a grid with images in Java.
I now draw the images in the Graphics2D component g as follows:
g.drawImage(image, 50 * cellWidth, 50 * cellHeight, cellWidth, cellHeight, Color.WHITE, null)
I'm now interested in rotating the image (while staying in the same grid row and column) 90 degrees in a given direction.
Could someone help me accomplish this?
First, you need a Graphics2D context. In most cases when supplied with a Graphics it's actually an instance of Graphics2D so you can simply cast it.
Having said that though, when perform transformations, it's always useful to create a new context (this copies the state only)...
Graphics2D g2d = (Graphics2D) g.create();
Next, you want to translate the origin point. This makes it a lot easier to do things like rotation.....
g2d.translate(50 * cellWidth, 50 * cellHeight);
Then you can rotate the context around the centre point of the cell (remember, 0x0 is now our cell offset)...
g2d.rotate(Math.toRadians(90), cellWidth / 2, cellWidth / 2);
And then we can simply draw the image...
g2d.drawImage(image, 0, 0, cellWidth, cellHeight, Color.WHITE, null);
And don't forget to dispose of the copy when you're done
g2d.dispose();
You might also want to take a look at The 2D Graphics trail, as you could use a AffineTransformation instead, but it'd be accomplishing the same thing, more or less
Is there a way to actually see the rotating happening (so see the rotation "live")?
Animation is a complex subject, add in the fact that Swing is single threaded and not thread safe and you need to think carefully about it.
Have a look at Concurrency in Swing and How to Use Swing Timers for more details.
Simple animation
The following example makes use of simple Swing Timer to rotate a image when it's clicked. The example makes use of time based approach (ie the animation runs over a fixed period of time). This produces a better result then a linear/delta approach.
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Simple {
public static void main(String[] args) throws IOException {
new Simple();
}
public Simple() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (IOException ex) {
Logger.getLogger(Advanced.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
}
public class TestPane extends JPanel {
private List<BufferedImage> images;
private BufferedImage selectedImage;
public TestPane() throws IOException {
images = new ArrayList<>(9);
for (int index = 0; index < 9; index++) {
BufferedImage img = ImageIO.read(getClass().getResource("/images/p" + (index + 1) + ".png"));
images.add(img);
}
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
if (selectedImage != null) {
return;
}
int col = (e.getX() - 32) / 210;
int row = (e.getY() - 32) / 210;
int index = (row * 3) + col;
selectedImage = images.get(index);
startTimer();
}
});
}
private Timer timer;
private Instant startedAt;
private Duration duration = Duration.ofSeconds(1);
private double maxAngle = 1440;
private double currentAngle = 0;
protected void startTimer() {
if (timer != null) {
return;
}
timer = new Timer(5, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (startedAt == null) {
startedAt = Instant.now();
}
Duration runtime = Duration.between(startedAt, Instant.now());
double progress = runtime.toMillis() / (double)duration.toMillis();
if (progress >= 1.0) {
progress = 1.0;
selectedImage = null;
startedAt = null;
stopTimer();
}
currentAngle = maxAngle * progress;
repaint();;
}
});
timer.start();
}
protected void stopTimer() {
if (timer == null) {
return;
}
timer.stop();
timer = null;
}
#Override
public Dimension getPreferredSize() {
return new Dimension((210 * 3) + 64, (210 * 3) + 64);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.translate(32, 32);
int row = 0;
int col = 0;
for (BufferedImage img : images) {
int x = col * 210;
int y = row * 210;
Graphics2D gc = (Graphics2D) g2d.create();
gc.translate(x, y);
if (selectedImage == img) {
gc.rotate(Math.toRadians(currentAngle), 210 / 2, 210 / 2);
}
gc.drawImage(img, 0, 0, this);
gc.dispose();
col++;
if (col >= 3) {
col = 0;
row++;
}
}
g2d.dispose();
}
}
}
nb: My images are 210x210 in size and I'm been naughty with not using the actual sizes of the images, and using fixed values instead
Advanced animation
While the above example "works", it becomes much more complicated the more you add it. For example, if you want to have multiple images rotate. Towards that end, you will need to keep track of some kind of model for each image which contains the required information to calculate the current rotation value.
Another issue is, what happens if you want to compound the animation? That is, scale and rotate the animation at the same time.
Towards this end, I'd lean towards using concepts like "time lines" and "key frames"
The following example is based on my personal library Super Simple Swing Animation Framework. This is bit more of a playground for me then a fully fledged animation framework, but it embodies many of the core concepts which help make animating in Swing simpler and help produce a much nicer result
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.time.Duration;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import org.kaizen.animation.Animatable;
import org.kaizen.animation.AnimatableAdapter;
import org.kaizen.animation.AnimatableDuration;
import org.kaizen.animation.DefaultAnimatableDuration;
import org.kaizen.animation.curves.Curves;
import org.kaizen.animation.timeline.BlendingTimeLine;
import org.kaizen.animation.timeline.DoubleBlender;
public class Advanced {
public static void main(String[] args) throws IOException {
new Advanced();
}
public Advanced() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (IOException ex) {
Logger.getLogger(Advanced.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
}
public class TestPane extends JPanel {
private List<BufferedImage> images;
private Map<BufferedImage, Double> imageZoom = new HashMap<>();
private Map<BufferedImage, Double> imageRotate = new HashMap<>();
private BlendingTimeLine<Double> zoomTimeLine;
private BlendingTimeLine<Double> rotateTimeLine;
public TestPane() throws IOException {
zoomTimeLine = new BlendingTimeLine<>(new DoubleBlender());
zoomTimeLine.addKeyFrame(0, 1.0);
zoomTimeLine.addKeyFrame(0.25, 1.5);
zoomTimeLine.addKeyFrame(0.75, 1.5);
zoomTimeLine.addKeyFrame(1.0, 1.0);
rotateTimeLine = new BlendingTimeLine<>(new DoubleBlender());
rotateTimeLine.addKeyFrame(0d, 0d);
rotateTimeLine.addKeyFrame(0.1, 0d);
// rotateTimeLine.addKeyFrame(0.85, 360.0 * 4d);
rotateTimeLine.addKeyFrame(1.0, 360.0 * 4d);
images = new ArrayList<>(9);
for (int index = 0; index < 9; index++) {
BufferedImage img = ImageIO.read(getClass().getResource("/images/p" + (index + 1) + ".png"));
images.add(img);
}
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
int col = (e.getX() - 32) / 210;
int row = (e.getY() - 32) / 210;
int index = (row * 3) + col;
BufferedImage selectedImage = images.get(index);
if (imageZoom.containsKey(selectedImage)) {
return;
}
animate(selectedImage);
}
});
}
protected void animate(BufferedImage img) {
Animatable animatable = new DefaultAnimatableDuration(Duration.ofSeconds(1), Curves.CUBIC_IN_OUT.getCurve(), new AnimatableAdapter<Double>() {
#Override
public void animationTimeChanged(AnimatableDuration animatable) {
double progress = animatable.getProgress();
Double desiredZoom = zoomTimeLine.getValueAt(progress);
imageZoom.put(img, desiredZoom);
double desiredAngle = rotateTimeLine.getValueAt(progress);
imageRotate.put(img, desiredAngle);
repaint();
}
#Override
public void animationStopped(Animatable animator) {
imageZoom.remove(img);
imageRotate.remove(img);
repaint();
}
});
animatable.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension((210 * 3) + 64, (210 * 3) + 64);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.translate(32, 32);
int row = 0;
int col = 0;
for (BufferedImage img : images) {
if (!(imageZoom.containsKey(img) || imageRotate.containsKey(img))) {
int x = col * 210;
int y = row * 210;
Graphics2D gc = (Graphics2D) g2d.create();
gc.translate(x, y);
gc.drawImage(img, 0, 0, this);
gc.dispose();
}
col++;
if (col >= 3) {
col = 0;
row++;
}
}
row = 0;
col = 0;
for (BufferedImage img : images) {
if (imageZoom.containsKey(img) || imageRotate.containsKey(img)) {
int x = col * 210;
int y = row * 210;
Graphics2D gc = (Graphics2D) g2d.create();
gc.translate(x, y);
double width = img.getWidth();
double height = img.getHeight();
double zoom = 1;
if (imageZoom.containsKey(img)) {
zoom = imageZoom.get(img);
width = (img.getWidth() * zoom);
height = (img.getHeight() * zoom);
double xPos = (width - img.getWidth()) / 2d;
double yPos = (height - img.getHeight()) / 2d;
gc.translate(-xPos, -yPos);
}
if (imageRotate.containsKey(img)) {
double angle = imageRotate.get(img);
gc.rotate(Math.toRadians(angle), width / 2, height / 2);
}
gc.scale(zoom, zoom);
gc.drawImage(img, 0, 0, this);
gc.dispose();
}
col++;
if (col >= 3) {
col = 0;
row++;
}
}
g2d.dispose();
}
}
}
nb: The paint workflow is a little more complicated (and could be optimised more) as it focuses on painting the images which are been animated onto of the others, which results in a much nicer result

How to draw rectangles from a JList Selection

I'm looking for some help regarding a Java program I'm writing. I need to display a simple bar graph consisting of rectangles based off of data saved in an array that has been generated by a JList selection event. However, nothing is drawn in the JPanel and I cannot figure out how to fix it.
List Selection Event (For reference)
try
{
BufferedReader in = new BufferedReader((new FileReader("scores.txt")));
String sLine;
while ((sLine = in.readLine()) != null)
{
vecAthlete.addElement(new Stage3_DataFile(sLine));
}
String[] saAthlete = new String[vecAthlete.size()];
for (int iI = 0; iI < vecAthlete.size(); iI++)
saAthlete[iI] = vecAthlete.get(iI).sName;
jList_Athlete.setListData(saAthlete);
jList_Athlete.setSelectedIndex(0);
// Reading input line by line
// Close file
in.close();
}
catch (IOException e)
{
System.out.println("ERROR: Could not read text file!");
}
jList_Athlete.addListSelectionListener(new javax.swing.event.ListSelectionListener()
{
public void valueChanged(javax.swing.event.ListSelectionEvent e)
{
double[][] daRun = new double[2][7];
final double dScoreUsed = 5;
double dMinScoreRun1 = 10, dMaxScoreRun1 = 0;
double dMinScoreRun2 = 10, dMaxScoreRun2 = 0;
double dTotalRun1 = 0, dTotalRun2 = 0;
jLabel_Athlete.setText(vecAthlete.get(jList_Athlete.getSelectedIndex()).sName);
jLabel_Country.setText(vecAthlete.get(jList_Athlete.getSelectedIndex()).sCountry);
jLabel_Run1_Score_1.setText(vecAthlete.get(jList_Athlete.getSelectedIndex()).saTempRun1[0]);
jLabel_Run1_Score_2.setText(vecAthlete.get(jList_Athlete.getSelectedIndex()).saTempRun1[1]);
jLabel_Run1_Score_3.setText(vecAthlete.get(jList_Athlete.getSelectedIndex()).saTempRun1[2]);
jLabel_Run1_Score_4.setText(vecAthlete.get(jList_Athlete.getSelectedIndex()).saTempRun1[3]);
jLabel_Run1_Score_5.setText(vecAthlete.get(jList_Athlete.getSelectedIndex()).saTempRun1[4]);
jLabel_Run1_Score_6.setText(vecAthlete.get(jList_Athlete.getSelectedIndex()).saTempRun1[5]);
jLabel_Run1_Score_7.setText(vecAthlete.get(jList_Athlete.getSelectedIndex()).saTempRun1[6]);
jLabel_Run2_Score_1.setText(vecAthlete.get(jList_Athlete.getSelectedIndex()).saTempRun2[0]);
jLabel_Run2_Score_2.setText(vecAthlete.get(jList_Athlete.getSelectedIndex()).saTempRun2[1]);
jLabel_Run2_Score_3.setText(vecAthlete.get(jList_Athlete.getSelectedIndex()).saTempRun2[2]);
jLabel_Run2_Score_4.setText(vecAthlete.get(jList_Athlete.getSelectedIndex()).saTempRun2[3]);
jLabel_Run2_Score_5.setText(vecAthlete.get(jList_Athlete.getSelectedIndex()).saTempRun2[4]);
jLabel_Run2_Score_6.setText(vecAthlete.get(jList_Athlete.getSelectedIndex()).saTempRun2[5]);
jLabel_Run2_Score_7.setText(vecAthlete.get(jList_Athlete.getSelectedIndex()).saTempRun2[6]);
daRun[0][0] = Double.parseDouble(jLabel_Run1_Score_1.getText());
daRun[0][1] = Double.parseDouble(jLabel_Run1_Score_2.getText());
daRun[0][2] = Double.parseDouble(jLabel_Run1_Score_3.getText());
daRun[0][3] = Double.parseDouble(jLabel_Run1_Score_4.getText());
daRun[0][4] = Double.parseDouble(jLabel_Run1_Score_5.getText());
daRun[0][5] = Double.parseDouble(jLabel_Run1_Score_6.getText());
daRun[0][6] = Double.parseDouble(jLabel_Run1_Score_7.getText());
daRun[1][0] = Double.parseDouble(jLabel_Run2_Score_1.getText());
daRun[1][1] = Double.parseDouble(jLabel_Run2_Score_2.getText());
daRun[1][2] = Double.parseDouble(jLabel_Run2_Score_3.getText());
daRun[1][3] = Double.parseDouble(jLabel_Run2_Score_4.getText());
daRun[1][4] = Double.parseDouble(jLabel_Run2_Score_5.getText());
daRun[1][5] = Double.parseDouble(jLabel_Run2_Score_6.getText());
daRun[1][6] = Double.parseDouble(jLabel_Run2_Score_7.getText());
for(int i = 0; i < 7; i++)
{
if(daRun[0][i] <= dMinScoreRun1)
{
dMinScoreRun1 = daRun[0][i];
}
if(daRun[0][i] >= dMaxScoreRun1)
{
dMaxScoreRun1 = daRun[0][i];
}
dTotalRun1 += daRun[0][i];
}
dTotalRun1 = (dTotalRun1 - dMinScoreRun1 - dMaxScoreRun1) / dScoreUsed;
jLabel_TotalRun1.setText(String.valueOf(dTotalRun1));
jLabel_Run1AddInfo.setText("Min Score: " + (dMinScoreRun1) + ", Max Score: " + (dMaxScoreRun1));
for(int i = 0; i < 7; i++)
{
if(daRun[1][i] <= dMinScoreRun2)
{
dMinScoreRun2 = daRun[1][i];
}
if(daRun[1][i] >= dMaxScoreRun2)
{
dMaxScoreRun2 = daRun[1][i];
}
dTotalRun2 += daRun[1][i];
}
dTotalRun2 = (dTotalRun2 - dMinScoreRun2 - dMaxScoreRun2) / dScoreUsed;
jLabel_TotalRun2.setText(String.valueOf(dTotalRun2));
jLabel_Run2AddInfo.setText("Min Score: " + (dMinScoreRun2) + ", Max Score: " + (dMaxScoreRun2));
if(dTotalRun1 >= dTotalRun2)
jLabel_FinalScore.setText(String.valueOf(dTotalRun1));
else
jLabel_FinalScore.setText(String.valueOf(dTotalRun2));
jPanel_Graph.repaint();
}
});
JPanel Definitions
JPanel jPanel_Graph = new JPanel();
jPanel_Graph.setBorder(new BevelBorder(BevelBorder.LOWERED, null, null, null, null));
jPanel_Graph.setBounds(729, 159, 700, 200);
frame.getContentPane().add(jPanel_Graph);
img = new BufferedImage(jPanel_Graph.getWidth(),
jPanel_Graph.getHeight(),
BufferedImage.TYPE_INT_RGB);
g2dimg = (Graphics2D)img.getGraphics();
// Draw a filled white coloured rectangle on the entire area to clear it.
g2dimg.setPaint(Color.WHITE);
g2dimg.fill(new Rectangle2D.Double(0, 0, img.getWidth(), img.getHeight()));
JPanel Drawing Event
class myJPanel extends JPanel
{
private Rectangle2D.Double rectangle;
public void paintComponent(Graphics g)
{
super.paintComponent(g);
for(int i = 0; i < 7; i++)
{
dHeight = daRun[0][i];
rectangle = new Rectangle2D.Double(dX, dY, dWidth, dHeight);
g2dimg.setPaint(Color.red);
g2dimg.draw(rectangle);
g2dimg.fill(rectangle);
dX += dXIncrement;
g2dimg.drawImage(img, 0, 0, null);
}
}
}
Thanks in advance!
So, from what I can tell, there is no way for your "graph" to know what it should paint. The basic idea is, your "graph" should be focused on painting the data/model, so you need some way to tell it when the data changes.
There are lots of ways you might do this, for example, you could devise a model, which you add/remove data to, which could trigger updates that the graph pane listeners for and then updates itself with the new data.
Or you just pass the data you want updated directly to the graph pane, which in this case, might the simplest solution available to you
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import javax.swing.DefaultListModel;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
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 GraphPane graphPane;
public TestPane() {
setLayout(new BorderLayout());
DefaultListModel<Double> listmodel = new DefaultListModel<>();
for (int index = 0; index < 100; index++) {
listmodel.addElement(Math.random());
}
graphPane = new GraphPane();
add(graphPane);
JList<Double> listData = new JList<>(listmodel);
add(new JScrollPane(listData), BorderLayout.WEST);
listData.addListSelectionListener(new ListSelectionListener() {
#Override
public void valueChanged(ListSelectionEvent e) {
graphPane.setData(listData.getSelectedValuesList().toArray(new Double[0]));
}
});
}
}
public class GraphPane extends JPanel {
private Double[] data;
public void setData(Double[] data) {
this.data = data;
repaint();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (data != null && data.length > 0) {
Graphics2D g2d = (Graphics2D) g.create();
double barWidth = getWidth() / (double)data.length;
int xPos = 0;
for (Double value : data) {
double height = (getHeight() - 1) * value;
Rectangle2D bar = new Rectangle2D.Double(xPos, getHeight() - height, barWidth, height);
g2d.setColor(Color.GREEN);
g2d.fill(bar);
g2d.setColor(Color.BLACK);
g2d.draw(bar);
xPos += barWidth;
}
g2d.dispose();
}
}
}
}

Javadrone - Unable to get image from Parrot AR.Drone 2.0

I am working on an app to control Parrot Ar.Drone 2.0 using Javadrone API and library.
I am able to connect into drone and make it take off/land successfully. There are 2 java files : DroneTestVideo.java, VideoPanel.java.
The DroneTestVideo.java file is responsible for connecting into drone and get data, while VideoPanel.java file serves as set images.
Javadrone API can be download here.
However, I have no idea why I'failing to get live images from drone. It just show me a black screen with the words:
"No video connection"
Here is my code:
DroneTestVideo.java
package dronetest;
import com.codeminders.ardrone.ARDrone;
import com.codeminders.ardrone.ARDrone.VideoChannel;
import com.codeminders.ardrone.DroneStatusChangeListener;
import com.codeminders.ardrone.NavData;
import com.codeminders.ardrone.NavDataListener;
import java.io.IOException;
import java.net.UnknownHostException;
import java.util.logging.Level;
import java.util.logging.Logger;
public class DroneTestVideo extends javax.swing.JFrame implements DroneStatusChangeListener, NavDataListener{
private static final long CONNECT_TIMEOUT = 10000L;
private ARDrone drone;
private final VideoPanel video = new VideoPanel();
public DroneTestVideo() {
initComponents();
initDrone();
videoPanel.add(video);
takeoff();
}
private void initDrone() {
try{
drone = new ARDrone();
drone.addStatusChangeListener(this);
drone.addStatusChangeListener(new DroneStatusChangeListener() {
public void ready() {
org.apache.log4j.Logger.getLogger(getClass().getName()).debug("updateLoop::ready()");
}
});
System.err.println("Configure");
drone.selectVideoChannel(ARDrone.VideoChannel.HORIZONTAL_ONLY);
drone.setCombinedYawMode(true);
drone.enableAutomaticVideoBitrate();
System.err.println("Connecting to the drone");
drone.connect();
drone.waitForReady(CONNECT_TIMEOUT);
drone.clearEmergencySignal();
System.err.println("Connected to the drone");
} catch (IOException ex) {
Logger.getLogger(DroneTestVideo.class.getName()).log(Level.SEVERE, null,ex);
}
drone.addNavDataListener(this);
video.setDrone(drone);
}
private void takeoff()
{
try {
System.err.println("**********\nTRIM\n**********");
drone.trim();
Thread.sleep(2000);
System.err.println("**********\nTAKEOFF\n**********");
drone.takeOff();
Thread.sleep(7000);
drone.land();
} catch (IOException ex) {
Logger.getLogger(DroneTestVideo.class.getName()).log(Level.SEVERE, null, ex);
} catch (InterruptedException ex) {
Logger.getLogger(DroneTestVideo.class.getName()).log(Level.SEVERE, null, ex);
}
}
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new DroneTestVideo().setVisible(true);
}
});
}
}
VideoPanel.java
package dronetest;
import com.codeminders.ardrone.ARDrone;
import com.codeminders.ardrone.DroneVideoListener;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
public class VideoPanel extends javax.swing.JPanel implements DroneVideoListener{
private AtomicReference<BufferedImage> image = new AtomicReference<BufferedImage>();
private AtomicBoolean preserveAspect = new AtomicBoolean(true);
private BufferedImage noConnection = new BufferedImage(320, 240, BufferedImage.TYPE_INT_RGB);
public VideoPanel() {
initComponents();
Graphics2D g2d = (Graphics2D) noConnection.getGraphics();
Font f = g2d.getFont().deriveFont(24.0f);
g2d.setFont(f);
g2d.drawString("No video connection", 40, 110);
image.set(noConnection);
}
public void setDrone(ARDrone drone) {
drone.addImageListener(this);
System.err.println("setDrone function here!");
}
public void setPreserveAspect(boolean preserve) {
preserveAspect.set(preserve);
}
public void frameReceived(BufferedImage im) {
image.set(im);
repaint();
}
public void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
int width = getWidth();
int height = getHeight();
drawDroneImage(g2d, width, height);
}
private void drawDroneImage(Graphics2D g2d, int width, int height) {
BufferedImage im = image.get();
if (im == null) {
return;
}
int xPos = 0;
int yPos = 0;
if (preserveAspect.get()) {
g2d.setColor(Color.BLACK);
g2d.fill3DRect(0, 0, width, height, false);
float widthUnit = ((float) width / 4.0f);
float heightAspect = (float) height / widthUnit;
float heightUnit = ((float) height / 3.0f);
float widthAspect = (float) width / heightUnit;
if (widthAspect > 4) {
xPos = (int) (width - (heightUnit * 4)) / 2;
width = (int) (heightUnit * 4);
} else if (heightAspect > 3) {
yPos = (int) (height - (widthUnit * 3)) / 2;
height = (int) (widthUnit * 3);
}
}
if (im != null) {
g2d.drawImage(im, xPos, yPos, width, height, null);
}
}
}
You are not updating your screen, so no new calls to drawDroneImage will be called into the VideoPanel.
You should set a loop to refresh the JPanel (call it repaint()).
And more, you need to implement DoubleBuffering Strategies to get a smoother image transaction.

Mirroring animated gif on load in Java - ImageIcon

So I have an animated gif that I load into an ImageIcon like this:
Image image = new ImageIcon("image.gif").getImage();
and I can draw it using this:
g.drawImage(image, x, y, null);
I know that I can mirror it on the fly using AffineTransform, but I need to be able to mirror it horizontally after loading, so that I can draw the mirrored one instead if needed without the overhead of transforming it every time it gets redrawn. Is there a way to do this using swing/awt?
A library that could do this would also be a huge help.
The problem is, as you have pointed out, is the fact the gif's are animated.
Unless you desperately want to take over the job of having to render each frame yourself, the only choice you have is to use an AffineTransform with in the paint method.
Generally speaking, you shouldn't see a significant difference (in rendering).
If you are really desperate, you could simply pre-render the gif externally and provide a mirrored version
Updated with a "kind of" working example
This is a combination of this and this answers, using this GIF writer.
Basically what this example does is it reads an original gif image, mirrors it frame by frame, and writes back out to a mirrored file.
It then loads both the original and mirrored files back in as ImageIcons, mostly because I'm not really up to re-inventing the wheel for display animated gifs. Yes, you could do it and everything you would need is provided within..
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.imageio.IIOException;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataNode;
import javax.imageio.stream.FileImageOutputStream;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageOutputStream;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
public class MirrorImage {
public static void main(String[] args) {
new MirrorImage();
}
public MirrorImage() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private ImageIcon orig;
private ImageIcon mirror;
public TestPane() {
mirror(new File("java_animated.gif"), new File("Mirror.gif"));
orig = new ImageIcon("java_animated.gif");
mirror = new ImageIcon("Mirror.gif");
}
#Override
public Dimension getPreferredSize() {
return mirror == null ? new Dimension(200, 200) : new Dimension(orig.getIconWidth(), orig.getIconHeight() * 2);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (orig != null) {
Graphics2D g2d = (Graphics2D) g.create();
int x = (getWidth() - orig.getIconWidth()) / 2;
int y = (getHeight() - (orig.getIconHeight() * 2)) / 2;
g2d.drawImage(orig.getImage(), x, y, this);
// AffineTransform at = new AffineTransform();
// at.setToScale(1, -1);
// at.translate(0, -mirror.getIconHeight());
// g2d.setTransform(at);
g2d.drawImage(mirror.getImage(), x, y + mirror.getIconHeight(), this);
g2d.dispose();
}
}
}
public static void mirror(File source, File dest) {
List<BufferedImage> images = new ArrayList<>(25);
List<Integer> delays = new ArrayList<>(25);
int delay = 0;
ImageOutputStream output = null;
GifSequenceWriter writer = null;
try {
String[] imageatt = new String[]{
"imageLeftPosition",
"imageTopPosition",
"imageWidth",
"imageHeight"
};
ImageReader reader = (ImageReader) ImageIO.getImageReadersByFormatName("gif").next();
ImageInputStream ciis = ImageIO.createImageInputStream(source);
reader.setInput(ciis, false);
int noi = reader.getNumImages(true);
BufferedImage master = null;
for (int i = 0; i < noi; i++) {
BufferedImage image = reader.read(i);
IIOMetadata metadata = reader.getImageMetadata(i);
Node tree = metadata.getAsTree("javax_imageio_gif_image_1.0");
NodeList children = tree.getChildNodes();
for (int j = 0; j < children.getLength(); j++) {
Node nodeItem = children.item(j);
System.out.println(nodeItem.getNodeName());
if (nodeItem.getNodeName().equals("ImageDescriptor")) {
Map<String, Integer> imageAttr = new HashMap<String, Integer>();
NamedNodeMap attr = nodeItem.getAttributes();
// for (int index = 0; index < attr.getLength(); index++) {
// Node node = attr.item(index);
// System.out.println("----> " + node.getNodeName() + "=" + node.getNodeValue());
// }
for (int k = 0; k < imageatt.length; k++) {
Node attnode = attr.getNamedItem(imageatt[k]);
imageAttr.put(imageatt[k], Integer.valueOf(attnode.getNodeValue()));
}
if (master == null) {
master = new BufferedImage(imageAttr.get("imageWidth"), imageAttr.get("imageHeight"), BufferedImage.TYPE_INT_ARGB);
}
Graphics2D g2d = master.createGraphics();
g2d.drawImage(image, imageAttr.get("imageLeftPosition"), imageAttr.get("imageTopPosition"), null);
g2d.dispose();
BufferedImage frame = mirror(copyImage(master));
ImageIO.write(frame, "png", new File("img" + i + ".png"));
images.add(frame);
} else if (nodeItem.getNodeName().equals("GraphicControlExtension")) {
NamedNodeMap attr = nodeItem.getAttributes();
Node delayNode = attr.getNamedItem("delayTime");
if (delayNode != null) {
delay = Math.max(delay, Integer.valueOf(delayNode.getNodeValue()));
delays.add(delay);
}
}
}
}
output = new FileImageOutputStream(dest);
writer = new GifSequenceWriter(output, images.get(0).getType(), delay * 10, true);
for (int i = 0; i < images.size(); i++) {
BufferedImage nextImage = images.get(i);
writer.writeToSequence(nextImage);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
writer.close();
} catch (Exception e) {
}
try {
output.close();
} catch (Exception e) {
}
}
}
public static BufferedImage mirror(BufferedImage img) {
BufferedImage mirror = createCompatibleImage(img);
Graphics2D g2d = mirror.createGraphics();
AffineTransform at = new AffineTransform();
at.setToScale(1, -1);
at.translate(0, -img.getHeight());
g2d.setTransform(at);
g2d.drawImage(img, 0, 0, null);
g2d.dispose();
return mirror;
}
public static BufferedImage copyImage(BufferedImage img) {
int width = img.getWidth();
int height = img.getHeight();
BufferedImage newImage = createCompatibleImage(img);
Graphics graphics = newImage.createGraphics();
int x = (width - img.getWidth()) / 2;
int y = (height - img.getHeight()) / 2;
graphics.drawImage(img, x, y, img.getWidth(), img.getHeight(), null);
graphics.dispose();
return newImage;
}
public static BufferedImage createCompatibleImage(BufferedImage image) {
return getGraphicsConfiguration().createCompatibleImage(image.getWidth(), image.getHeight(), image.getTransparency());
}
public static GraphicsConfiguration getGraphicsConfiguration() {
return GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
}
public static class GifSequenceWriter {
protected ImageWriter gifWriter;
protected ImageWriteParam imageWriteParam;
protected IIOMetadata imageMetaData;
/**
* Creates a new GifSequenceWriter
*
* #param outputStream the ImageOutputStream to be written to
* #param imageType one of the imageTypes specified in BufferedImage
* #param timeBetweenFramesMS the time between frames in miliseconds
* #param loopContinuously wether the gif should loop repeatedly
* #throws IIOException if no gif ImageWriters are found
*
* #author Elliot Kroo (elliot[at]kroo[dot]net)
*/
public GifSequenceWriter(
ImageOutputStream outputStream,
int imageType,
int timeBetweenFramesMS,
boolean loopContinuously) throws IIOException, IOException {
// my method to create a writer
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);
}
/**
* Close this GifSequenceWriter object. This does not close the underlying
* stream, just finishes off the GIF.
*/
public void close() throws IOException {
gifWriter.endWriteSequence();
}
/**
* Returns the first available GIF ImageWriter using
* ImageIO.getImageWritersBySuffix("gif").
*
* #return a GIF ImageWriter object
* #throws IIOException if no GIF image writers are returned
*/
private static ImageWriter getWriter() throws IIOException {
Iterator<ImageWriter> iter = ImageIO.getImageWritersBySuffix("gif");
if (!iter.hasNext()) {
throw new IIOException("No GIF Image Writers Exist");
} else {
return iter.next();
}
}
/**
* Returns an existing child node, or creates and returns a new child node
* (if the requested node does not exist).
*
* #param rootNode the <tt>IIOMetadataNode</tt> to search for the child
* node.
* #param nodeName the name of the child node.
*
* #return the child node, if found or a new node created with the given
* name.
*/
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);
}
}
}
Caveats
The Gif writer currently only works with fixed rate gifs. It should be possible to change this, but I didn't have the time.
Basically, as I understand it, you would need to pass a "frame" delay to the writeToSquence method. Within this method you would need to construct an appropriate IIOMetadata with all the required properties, plus your frame delay...
Updated after playing with original gif
The GIF I was playing with was optimised. That is, each frame "added" to the animation, rather then being a brand new frame. Yours is the other way round. Each frame is an entire image.
Now, there are probably lots of ways you could check for this, but right now, I can't be bothered...
Instead...in the mirror(File, File) method, I changed it so that rather then using a single "master" image, each frame creates a new BufferedImage
BufferedImage frame = new BufferedImage(imageAttr.get("imageWidth"), imageAttr.get("imageHeight"), BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = frame.createGraphics();
g2d.drawImage(image, imageAttr.get("imageLeftPosition"), imageAttr.get("imageTopPosition"), null);
g2d.dispose();
frame = mirror(frame);
ImageIO.write(frame, "png", new File("img" + i + ".png"));
images.add(frame);
I also updated the GifSequenceWriter to set the meta data to more closely match the original as well...
graphicsControlExtensionNode.setAttribute("disposalMethod", "restoreToBackgroundColor");
graphicsControlExtensionNode.setAttribute("userInputFlag", "FALSE");
graphicsControlExtensionNode.setAttribute(
"transparentColorFlag",
"TRUE");
..overhead of transforming it every time..
That overhead is just about 0. But if you don't want to use AffineTransform simply change the x,y in a loop.
See also Show an animated BG in Swing for more tips.
Note
This:
g.drawImage(image, x, y, null);
Should be:
g.drawImage(image, x, y, this); // containers are typically an ImageObserver!
For non-animated images, you can create a mirrored image.
// Width and height
int w = image.getWidth(null);
int h = image.getHeight(null);
// Create a new BufferedImage
BufferedImage mirror = new BufferedImage(w, h);
// Draw the image flipping it horizontally
Graphics2D g = mirror.createGraphics();
g.drawImage(image, 0, 0, w, h, w, 0, 0, h, null);
// Dispose the graphics.
g.dispose();
You can then use mirror which is already mirrored horizontally.

How to efficiently draw a subpart of an image in Swing

Let's say I have a BufferedImage of type TYPE_4BYTE_ABGR in Swing and I want to draw only a part of it. For example I would like to draw the left half only or some triangular shape or something more complicated.
Reason is that the final image shall be composed from subparts of individual images I have.
What's the best way to do that?
I would prefer to define a polygon and then use this shape as a mask for drawing, if this is possible.
My current idea: make a copy of the individual image and set all pixels outside the wished shape to transparent, then draw the whole image. I think this might work but might be too slow with the copying and all.
edit:
I tested the solution of Guillaume and found that it works and does not extremely slow down the painting. Using a clip resulted in an increase of drawing time from 14ms to 35ms but these times are very inaccurate. I used profiling the EDT from here. Here is the code.
import java.awt.AWTEvent;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
/**
*
*/
public class ClipTilesTest {
// tile size and number of tiles in each row/column
private static int TILE_SIZE = 100;
private static int TILE_NUM = 6;
// taken from https://stackoverflow.com/questions/5541493/how-do-i-profile-the-edt-in-java-swing
public static class TimedEventQueue extends EventQueue {
#Override
protected void dispatchEvent(AWTEvent event) {
long startNano = System.nanoTime();
super.dispatchEvent(event);
long endNano = System.nanoTime();
if (endNano - startNano > 5000000) {
System.out.println(((endNano - startNano) / 1000000) + "ms : " + event);
}
}
}
private static void initUI() {
Toolkit.getDefaultToolkit().getSystemEventQueue().push(new TimedEventQueue());
// download image
BufferedImage image;
try {
image = ImageIO.read(new URL("http://download.chip.eu//ii/163859211_4b28e1e687.jpg"));
} catch (IOException ex) {
ex.printStackTrace();
return;
}
// take out small chunk
final BufferedImage tile = image.getSubimage(0, 0, TILE_SIZE, TILE_SIZE);
JFrame frame = new JFrame();
frame.setTitle(ClipTilesTest.class.getSimpleName());
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
// the panel containing some tiles
JPanel view = new JPanel() {
#Override
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
for (int i = 0; i < TILE_NUM; i++) {
for (int j = 0; j < TILE_NUM; j++) {
// version 1
/*
g2d.setClip(i * TILE_SIZE, j * TILE_SIZE , (i+1)*TILE_SIZE, (j+1)*TILE_SIZE);
g2d.drawImage(tile, i * TILE_SIZE, j * TILE_SIZE, null);
*/
// version 2
g2d.setClip(i * TILE_SIZE, j * TILE_SIZE , i*TILE_SIZE + TILE_SIZE/2, (j+1)*TILE_SIZE);
g2d.drawImage(tile, i * TILE_SIZE, j * TILE_SIZE, null);
g2d.setClip(i * TILE_SIZE + TILE_SIZE/2, j * TILE_SIZE , (i+1)*TILE_SIZE , (j+1)*TILE_SIZE);
g2d.drawImage(tile, i * TILE_SIZE, j * TILE_SIZE, null);
}
}
}
};
view.setPreferredSize(new Dimension(TILE_SIZE * TILE_NUM, TILE_SIZE * TILE_NUM));
// add, pack, set visible
frame.add(view);
frame.pack();
frame.setVisible(true);
// now make a repaint event, so we can start measuring
view.repaint();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
ClipTilesTest.initUI();
}
});
}
}
One easy way to achieve this effect, is to modify the "clip" of the Graphics object and to set it to the shape you want to draw.
I don't know how efficient this is, but you could consider caching the clipped image and then draw the entire cached image.
Here is a small demo code:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.Ellipse2D;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class TestClippedPanel {
private static class ClippedPanel extends JPanel {
private ImageIcon image;
private List<Shape> shapes;
public ClippedPanel() throws MalformedURLException {
shapes = new ArrayList<Shape>();
image = new ImageIcon(new URL("http://download.chip.eu//ii/163859211_4b28e1e687.jpg"));
Random random = new Random();
for (int i = 0; i < 10; i++) {
int x = random.nextInt(image.getIconWidth() - 1);
int y = random.nextInt(image.getIconHeight() - 1);
int w = random.nextInt(image.getIconWidth() - x) + 1;
int h = random.nextInt(image.getIconHeight() - y) + 1;
shapes.add(new Rectangle(x, y, w, h));
}
for (int i = 0; i < 10; i++) {
int x = random.nextInt(image.getIconWidth() - 1);
int y = random.nextInt(image.getIconHeight() - 1);
int w = random.nextInt(image.getIconWidth() - x) + 1;
int h = random.nextInt(image.getIconHeight() - y) + 1;
shapes.add(new Ellipse2D.Double(x, y, w, h));
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Image img = image.getImage();
for (Shape shape : shapes) {
((Graphics2D) g).setClip(shape);
g.drawImage(img, 0, 0, this);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(image.getIconWidth(), image.getIconHeight());
}
}
protected void initUI() throws MalformedURLException {
final JFrame frame = new JFrame(TestClippedPanel.class.getSimpleName());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final ClippedPanel panel = new ClippedPanel();
frame.add(panel);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
try {
new TestClippedPanel().initUI();
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
}

Categories

Resources