Why gif animation doesn't animate when using it in paintComponent()? - java

I'm using paintComponent() to paint a gif animated image at the backgound of JPanel.
It shows up the gif but doesn't animate.
I use java 1.5 and i know that i can use label with icon.
Does any body know why and how to fix it?
private static class CirclePanel extends JPanel {
ImageIcon imageIcon = new ImageIcon(BarcodeModel.class.getResource("verify.gif"));
Point point = f.getLocation();
protected void paintComponent(Graphics g) {
Graphics gc = g.create();
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.3f));
g2d.setColor(Color.BLUE);
g2d.drawImage(imageIcon.getImage(), getWidth() / 2, getHeight() / 2, null);
g2d.drawRect(0, 0, getWidth(), getHeight());
g2d.setStroke(new BasicStroke(10f,BasicStroke.CAP_ROUND,BasicStroke.JOIN_MITER));
g2d.setFont(g.getFont().deriveFont(Font.BOLD | Font.ITALIC,15f));
g2d.drawString("Wait Please ...",getWidth()/2-imageIcon.getIconHeight()/3,getHeight()/2+imageIcon.getIconHeight()+15);
g2d.dispose();
}
This is the gif image.
Edited: just add image observer to the g2d.drawImage() method.
g2d.drawImage(imageIcon.getImage(), getWidth() / 2, getHeight() / 2, this);

The reason is that the standard Java ImageIO API only loads the first image of the gif.
How to fix? Google for a Gif Loader for Java, which loads every image of the gif. Then you have to paint the right image at the right time. An alternative way would be to have different png files representing each time one frame of the animation.
Update: Well... Actually, after doing some research, it looks like the way you did it actually loads all the frames of the animated gif. The reason for it is that the ImageIcon's method getImage() always returns the first image.
To fix it, you can try this (I'm not sure if it will work...)
Instead of using Grahpics.drawImage(), use ImageIcon.paintIcon(). Like this:
imageIcon.paintIcon(this, g2d, getWidth() / 2 - imageIcon.getIconWidth() / 2, getHeight() / 2);

Image observer will take care of it.
You should just pas the observer to the g2d.drawImage(); as below.
g2d.drawImage(imageIcon.getImage(), getWidth() / 2, getHeight() / 2, this);
In my case 'this' refer to CirclePanel which extends JPanel, it can be any thing for example if you are using gif as a icon for a button, you should use button as image observer.

Related

How can I paint an image from BufferStrategy to Png file?

I've created a Java program that generates snowflakes and I'd like to save the image created as a .png file once the program finishes drawing.
I've searched on Internet, but I've found only programs using BufferedImage, while I use a BufferStrategy, so I don't know exactly where to start.
The draw method in my program uses a BufferStrategy to create the Graphics component.
For example, to draw a simple line the method is:
bs = display.getCanvas().getBufferStrategy();
if (bs == null) {
display.getCanvas().createBufferStrategy(3);
return;
}
g = bs.getDrawGraphics();
g.clearRect(0, 0, width, height);
g.setColor(Color.BLACK);
g.drawLine(0, 0, 50, 50);
What I would like is to get an exact copy of what has been drawn on the screen by the program to be saved as a .png image.
Hope you can help me.
Why not take a screenshot and then past it onto MS paint or some other(and better) image editing software like Photoshop or fire alpaca? That should solve your problem.
The common denominator between BufferedStrategy and BufferedImage is Graphics, so you want to write a paint routine so that you can simply pass a reference of Graphics to it
public void render(Graphics g) {
g.clearRect(0, 0, width, height);
g.setColor(Color.BLACK);
g.drawLine(0, 0, 50, 50);
}
Then you can pass what ever context you want.
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_RGB);
Graphics2D g2d = img.createGraphics();
render(g2d);
g2d.dispose();
Then you can use ImageIO.write to write the image to disk. See Writing/Saving an Image for more details

RadialGradientPaint Equivalent in Android

I am drawing a radial gradient circle on an image like this
I have java code for this
private void drawRadialGradientCircleJava(String imagePath, double posX, double posY, float radius, String outputPath) throws IOException{
BufferedImage city = ImageIO.read(new File(imagePath));
BufferedImage mask = new BufferedImage(city.getWidth(), city.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = mask.createGraphics();
Color transparent = new Color(255, 0, 0, 0);
Color fill = Color.RED;
RadialGradientPaint rgp = new RadialGradientPaint(
new Point2D.Double(posX, posY),
radius,
new float[]{0f, 0.75f, 1f},
new Color[]{transparent, transparent, fill});
g2d.setPaint(rgp);
g2d.fill(new Rectangle(0, 0, mask.getWidth(), mask.getHeight()));
g2d.dispose();
BufferedImage masked = new BufferedImage(city.getWidth(), city.getHeight(), BufferedImage.TYPE_INT_ARGB);
g2d = masked.createGraphics();
g2d.setColor(Color.RED);
g2d.fillRect(0, 0, masked.getWidth(), masked.getHeight());
g2d.drawImage(city, 0, 0, null);
g2d.setComposite(AlphaComposite.DstAtop);
g2d.drawImage(mask, 0, 0, null);
g2d.dispose();
ImageIO.write(masked,"png", new File(outputPath));
}
I want to do same thing in Android, I have an image view in which I have an image, now I want to touch a point in image and draw this transparent circle around that point
I have following Android code as well but id doesn't draw anything on the image
private void drawRadialGradientCircleAndroid(ImageView imageView, float posX,
float posY, float radius) throws IOException {
RadialGradient gradient = new RadialGradient(posX, posY, radius, Color.TRANSPARENT,
Color.TRANSPARENT, android.graphics.Shader.TileMode.CLAMP);
Paint p = new Paint();
p.setDither(true);
p.setShader(gradient);
Bitmap bm = ((BitmapDrawable) imageView.getDrawable()).getBitmap();
Bitmap bmOverlay = Bitmap.createBitmap(bm.getWidth(), bm.getHeight(), bm.getConfig());
Canvas canvas = new Canvas(bmOverlay);
canvas.drawBitmap(bm, new Matrix(), null);
canvas.drawCircle(posY, posX, radius, p);
imageView.setImageBitmap(bmOverlay);
}
Please help how can I achieve this in Android.
We should migrate this to the answer boxes.
OP has basically got it here- and in fact the OP's revised gist is brilliant.
Some general tips regarding the first attempt in the question:
1) In protected void onSizeChanged(int w, int h, int oldw, int oldh):
width = w; there is no reason why you can't call getWidth() when you require this. The reason it's advisable is because the View's internal width is set quite late after onMeasure. Consequently, onDraw may be the next time you want a most up to date version, so use the getter there.
mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);. Creating a bitmap is an expensive and memory intensive operation. Unless you want to write a bitmap to a file, or send it to a BitmapDrawable for an ImageView or something, you don't need to do this. Especially with effects drawn onto the UI with android's graphics library.
mCanvas = new Canvas(mBitmap); followed by a draw operation onto the new canvas. This is never needed. And yet I've seen it (not work) in many code bases and attempts. I think it's the fault of an old stack overflow post that got people doing this so that they could transform a canvas on a custom view without effecting the drawing onto the rest of the canvas. Incidentally, if you need this, use .restore() and .save() instead. If you see new Canvas, be suspicious.
2) onDraw(...):
Yes, you need to avoid doing things in onDraw, like, creating objects, or any heavy processing. But you still need to do the things in onDraw you need to do in onDraw!
So here you simply need to call : canvas.drawCircle(float cx, float cy, float radius, Paint paint) with arguments as per the docs.
This really isn't that sinful for onDraw. If you're worried about calling this too much, as might be the case if your entire button is animating across the screen, you need to use hardware acceleration available in later API versions, as will be detailed in an article called Optimizing the View; very helpful reading if you're using lots of custom drawn views.
3) That pesky radial gradient. The next issue you had is that you quite rightly created your paint in an initmethod so that the object creation was off the draw. But then quite rightly it will have IllegalArgumentExceptioned (I think) on you because at that stage the getHeight() of the view was 0. You tried passing in small pixel values- that won't work unless you know some magic about screen sizes.
This isn't your issue as much as the annoying view cycle at the heart of Android's design patterns. The fix though is easy enough: simply use a later part of the view's drawing process after the onMeasure call to set the paint filter.
But there are some issues with getting this right, namely that sometimes, annoyingly, onDraw gets called before the point at which you'd expect it. The result would be your paint is null and you wouldn't get the desired behavior.
I have found a more robust solution is simply to do a cheeky and naughty little null check in the onDraw and then once only construct the paint object there. It's not strictly speaking optimal, but given the complex way in which the Paint objects hook up with Android's graphics native layer better than trying to straddle the paint configuration and construction in many frequently called places. And it makes for darn clearer code.
This would look like (amending your gist):
#Override
protected void onDraw(final Canvas canvas) {
super.onDraw(canvas);
if (mPaint == null) {
mPaint = new Paint();
mPaint.setColor(Color.BLACK);
mPaint.setStrokeWidth(1);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setShader(new RadialGradient(getWidth() / 2, getHeight() / 2,
getHeight() / 3, Color.TRANSPARENT, Color.BLACK, TileMode.MIRROR));
}
width = getWidth();
height = getHeight();
canvas.drawCircle(width / 2, height / 2, height / 3, mPaint);
}
So note a few changes- I think from your description you want the two colours swapped round in the arguments, also don't forget to center the center of your gradient in your view: width/2 and height/2 arguments.
Best of luck!

Java Graphics2D Image Moving

So in this block of code, a 40x40 square can move across a window by calling directional methods, and I'm trying to get a spaceship to appear instead of the square. No matter what I try, it just isn't working.
public void paintComponent (Graphics g) {
ImageIcon wallpaper = new ImageIcon("images/JGalagawallpaper.png");
image = wallpaper.getImage();
g.drawImage(image, 400, 400, null);
ImageIcon ship = new ImageIcon("images/galaga.png");
galaga = ship.getImage();
super.paintComponent(g);
Graphics2D graphic = (Graphics2D) g;
graphic.fill(new Rectangle.Double(x, y, 40, 40));
//graphic.drawImage(galaga, x, y, 40, 40);
}
My question is, how do I get that thing to appear? I already tried tinkering with graphic.drawImage, however that didn't really work out as well as I hoped. That's what the commented out code is.
g.drawImage(image, 400, 400, null);
First you draw the image.
super.paintComponent(g);
Then you invoke the above code which is used to pint the background color of the panel, thus overwriting the image. The above statement should be the first statement of the painting method.
ImageIcon wallpaper = new ImageIcon("images/JGalagawallpaper.png");
A painting method is for painting only. Don't do I/O in the method. The image should be read in the constructor of your class so that it is only read once, not every time the component is repainted.
You also need to look at the coordinates of where you paint the image. Maybe the panel is not that big?
Did you verify that the image was read properly by display its size?

How does double buffering in applets work?

I'm writing this Java code from a video tutorial in youtube on making Java game applets. However, the instructor didn't really explain how an applet's update method does double-buffering as he calls it.
import java.awt.*;
import java.applet.Applet;
public class Game extends Applet implements Runnable {
private Image i;
private Graphics g2;
private int x, y, dx = 1, dy = 1, radius = 10;
...
public void update(Graphics g) {
if ( i == null ) {
i = createImage(getHeight(), getHeight());
}
g2 = i.getGraphics();
g2.setColor(getBackground());
g2.fillRect(0, 0, getWidth(), getHeight());
g2.setColor(getForeground());
paint(g2);
g.drawImage(i, 0, 0, null);
}
public void paint(Graphics g)
{
x+= dx;
y+= dy;
g.setColor(Color.RED);
g.fillOval( x, getHeight() - y, radius * 4, radius * 4);
g.setColor(Color.BLUE);
g.fillOval( getWidth() - x - radius, y, radius * 4, radius * 4);
g.setColor(Color.GREEN);
g.fillOval( x, y, radius * 4, radius * 4);
g.setColor(Color.YELLOW);
g.fillOval( getWidth() - x , getHeight() - y , radius * 4, radius * 4);
}
How is flickering being removed here? What is the use of having an Image object? What is the use of having another Graphics object, why not use the parameter Graphics?
The general idea of double buffering is that drawing is slow, and if you have a lot to draw the user will notice this in the form of flickering. Instead, you do all your drawing to an off screen image (a buffer). Then when you are ready, you swap the offscreen buffer so it is now drawing on screen. This swap happens very quickly, since it's usually just updating a pointer.
The user no longer sees the flickering because all the drawing work is done off screen.
The code you posted is a variant of double buffering. All the drawing work is done off screen to the Image object i. Once the drawing is complete, the image object is then copied to the component, in the last line of the update method.
I say variant because the code above does not swap the buffers. Instead, you are copying the off screen buffer i to the on screen one. It still eliminates flicker because all the rendering work is done off screen. Copying the image is still quite fast.
The second Graphics object is there because you should always use the one provided by the component when drawing. So, the code above asks the Image object for its Graphics object. However, there is no real reason to store g2 as a member variable, you can just request it each time in update.

Painting a rounded border onto a JScrollPane

I am trying to paint a rounded rectangle around a JScrollPane. For the life of me I can't figure out how to do this! No matter what I try, the border is not visible. I have figured out that it is drawing BEHIND the contents and not over them. The only thing inside the scroll pane is a JPanel with some graphics painted onto it. Does anyone know how to fix this?
Here is the code I have tried to paint the border on the scroll pane:
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(Color.black);
g2.setStroke(new BasicStroke(1));
g2.draw(new RoundRectangle2D.Double(0, 0, getWidth() - 1, getHeight() - 1, 10, 10));
}
I have also tried using paint instead of paintComponent but with no such luck!
You are actually painting outside of the components bounds, which is a big no-no, and is why you are having this problem. You should consider creating a custom Border or extending the component insets so that you have room to paint your outline
This needs nothing custom. Simply use a LineBorder(lineColor,thickness,roundedCorners)..
Creates a line border with the specified color, thickness, and corner shape.
Where..
roundedCorners - whether or not border corners should be round

Categories