I am writing a game as a java applet using the Java2D libraries along with the BufferedImage class. The game stutters (2-3 seconds) the very first time an image is drawn. The BufferImage files are all loaded before the game begins, using this method:
bufferedImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
try {
URL url = new URL(a.getCodeBase(), "images//smoke5frames.png");
bufferedImage = ImageIO.read(url);
} catch (IOException e) { e.printStackTrace(); }
mSmoke = bufferedImage;
To draw the image, I am using this method:
public void draw(Graphics2D g) {
g.drawImage(mTextures[iCurrentFrame], mAffineTransform, null); }
The images in question are SheetedSprites, typically cut from a sprite sheet using:
for each frame in animation...
myBufferedImage.getSubimage(i * iFrameWidth, 0, iFrameWidth, iHeight);
Which is done during game play when the entity is created. Removing this line did not solve the problem, so I am assuming (perhaps poorly) getSubimage() isn't expensive enough to cause the issue.
I could loop though each and every frame of animation and draw them all for a quick-fix, but I'd like to understand more about the problem and why it happens.
The kicker is that once the SheetedSprite has been drawn once, you can refresh the page and the problem doesn't occur again. The game runs perfectly smoothly after everything has been drawn before. This is the case in both IE and Chrome (I haven't tried it on any other browsers). As a side note, the problem never occurs in Eclipse's Applet Viewer.
My best guess is that the images are somehow cached by the browser, though really I'm stumped. I haven't tried loading/drawing images with a different set of graphical libraries either (Slick2D, LWJGL, etc). Thanks for any suggestions!
I had this problem about 2 years ago, and I found a fix for it on the great Java-gaming.org. The problem for me, was that Java has a problem with certain kinds of png-files. Some would cause lag when utilized for the first time, and some wouldn't. This "fixer-upper-method" takes a BufferedImage, and returns a BufferedImage, but in between, it does some magic, that makes the image easier for Java to decode (don't ask me how).
Enjoy!
private BufferedImage toCompatibleImage(BufferedImage image)
{
if(image==null)System.out.println("Image is null");
// obtain the current system graphical settings
GraphicsConfiguration gfx_config = GraphicsEnvironment.
getLocalGraphicsEnvironment().getDefaultScreenDevice().
getDefaultConfiguration();
/*
* if image is already compatible and optimized for current system
* settings, simply return it
*/
if (image.getColorModel().equals(gfx_config.getColorModel()))
return image;
// image is not optimized, so create a new image that is
BufferedImage new_image = gfx_config.createCompatibleImage(
image.getWidth(), image.getHeight(), image.getTransparency());
// get the graphics context of the new image to draw the old image on
Graphics2D g2d = (Graphics2D) new_image.getGraphics();
// actually draw the image and dispose of context no longer needed
g2d.drawImage(image, 0, 0, null);
g2d.dispose();
// return the new optimized image
return new_image;
}
Related
I need to draw strings on a painted image. The project I am working on also requires the string to move around the screen at least 22 times in a second. Hence, its position can be anywhere on the image. So, redrawing the image with the string on it won't be possible as I feel there are better ways of doing this and that would unnecessarily consume resources redrawing the whole image. I have also tried using panel.getGraphics and then painting on the image but then all the drawn text is all over the screen(the code is below). I was wondering if someone could guide me in the right direction on how I can draw text over a paintedImage but it also needs to reset its position when required. The code I have tried that doesn't reset its previous position is below.
Original Panel with the Image:
public class PanelForImages extends JPanel{
private BufferedImage image;
public PanelForImages(File image) throws IOException{
//this.image = image;
//URL resource = getClass().getResource("so2.jpg");
this.image = ImageIO.read(image);
}
#Override
public void paintComponent(Graphics g){
//super.paint(g);
//super.paintComponents(g);
super.paintComponent(g);
//g.drawImage(image, 3, 4, this);
g.drawImage(image, 0, 0, getWidth(), getHeight(), this);
g.drawString("HELLOOOOOOOOOOOOOOOOOOOOOOOO", 400, 400);
//repaint();
}
}
Method with which I am trying to draw the string over the image.
public void drawFixationsOnFrame(GazeData gazeData){
Graphics g = this.panelForImages.getGraphics();
g.drawString("TEsting 123", (int)gazeData.smoothedCoordinates.x, (int)gazeData.smoothedCoordinates.y);
g.dispose();
jF.revalidate();
}
I have also tried making a new panel and then adding it to the current one but it doesn't seem to work. I am not sure how I can make it so it comes on top of the panelForImages without it hiding panelForImages.
The project I am working on also requires the string to move around the screen at least 22 times in a second. Hence, its position can be anywhere on the image. So, redrawing the image with the string on it won't be possible as I feel there are better ways of doing this and that would unnecessarily consume resources redrawing the whole image.
Then don't redraw the whole image. The JComponent repaint(...) method has an override, one that allows you to repaint only a select rectangle (check out the JComponent API for more on this). You will want to move the image in a Swing Timer, then repaint its old location (to get rid of the old String image) and repaint its new location. If you need the dimensions of the String, then use FontMetrics to help you get its bounding rectangle.
I note that your code has two other issues that are worrisome, some of it commented out:
Calling getGraphics() on a component to get its graphics component and draw with it -- you don't want to do this as this will result in a Graphics object that is short-lived risking broken images or a NPE
Calling repaint() from within the paintComponent. This is a bad and completely uncontrolled way of doing animation. Instead use a Swing Timer so you can have complete control of the animation and so you don't use paintComponent for something it was not intended to be used for.
I am playing around with graphics in Java using JFrames and am trying to completely understand when Graphics changes actually occur.
Let's say I have a timer running every second, and every second the graphics change. The tick method, for example, will run every second...
public void tick(Graphics g) {
g.fillRect(0,0,10,10);
//Do Some Computations
g.fillRect(0,0,someComputedWidth,someComputedHeight);
}
Are the graphics instantly updated on the computers display? When the first fillRect method is called, is that rectangle instantly created?
I want to be able to draw all my graphics, and then make them have their effect at the same exact time. I want the tick method to buffer all updates, and then execute them all at once. How do I accomplish this?
To buffer your updates you can use BufferedImage:
#Override
public void paintComponent(Graphics g){
BufferedImage image = new BufferedImage(this.size.width, this.size.height, BufferedImage.TYPE_INT_RGB);
Graphics bgr = image.getGraphics();
for(Tile t : this.tiles){
t.draw(bgr);
}
super(bgr);
g.drawImage(image, 0, 0, null);
bgr.dispose();
image = null;
}
I used this method in one of my school projects. It draws to an off-screen image and then draws the image to display.
With few drawable objects being drawn, and no, or little animation, updating directly to the display does not always have noticable effects, however with more complex tasks the image "flickers" and double buffering is a perfect cure for that.
I've got a simple Swing/AWT application that runs in full screen mode on Windows. I have a couple of different PNG files that it loads as its own background image depending on context.
It loads them like this:
BufferedImage bufferedImage;
bufferedImage = ImageIO.read(getClass().getResource("/bg1.png"));
Image bgImage1 = bufferedImage.getScaledInstance(width, height, Image.SCALE_SMOOTH);
bufferedImage = ImageIO.read(getClass().getResource("/bg2.png"));
Image bgImage2 = bufferedImage.getScaledInstance(width, height, Image.SCALE_SMOOTH);
bufferedImage = ImageIO.read(getClass().getResource("/bg3.png"));
Image bgImage3 = bufferedImage.getScaledInstance(width, height, Image.SCALE_SMOOTH);
And later draws them like this:
window.repaint();
graphics.drawImage(bgImage1, 0, 0, null);
// draw some other stuff too, like text
And just to be thorough, window is a JWindow variable, and graphics is a Graphics2D variable.
The problem I'm running into happens when I switch out one of the background images for another. The first time I do the switch, calling something like this:
window.repaint();
graphics.drawImage(bgImage2, 0, 0, null);
// draw some other stuff too, like text
...the entire screen goes white for about a second. And then it does successfully show the image, but that flicker is really annoying. My guess is that because the images are relatively large and high resolution (2560x1440), it needs about a second to load them and scale them to the appropriate size.
How can I get it to load those images silently? As in... how do I avoid drawing a blank white screen for a second, that first time it displays a new background image? All subsequent times are already instantaneous, probably because it's truly grabbed them into memory at that point. But simply calling getScaledInstance apparently isn't enough to put things into memory, because it doesn't actually flicker until I call drawImage down the line.
ImageIcon will load in the background as a feature.
You can also accomplish this fairly easily with a background thread e.g.:
final String path = "/example.png";
new SwingWorker<BufferedImage, Void>() {
#Override
public BufferedImage doInBackground() throws IOException {
return ImageIO.read(ClassName.class.getResource(path));
}
#Override
public void done() {
try {
BufferedImage img = get();
// put img somewhere
} catch(InterruptedException ignored) {
} catch(ExecutionException ex) {
ex.printStackTrace(System.err);
}
}
}.execute();
Also,
window.repaint();
graphics.drawImage(bgImage2, 0, 0, null);
This worries me a little bit. In general we do not need to ask for a repaint of an entire top-level container. You should also not be using getGraphics() to paint.
Non-top-level Swing components are double-buffered but if you are painting outside of the paint structure you do not get this. It will result in flickering.
Two good sources for correct custom painting are:
Lesson: Performing Custom Painting
Painting in AWT and Swing
Painting should be done passively by overriding paintComponent on a JComponent. A JLabel can also display an ImageIcon as a feature so you do not necessarily have to do custom painting if you just want to display an image.
I notice that the scaling takes a lot of time, not the loading of the images.
Because of this, I usually store a scaled image so I can use it later on so I only have to scale once.
I want to load some images to the cloud, but I want to add some protections when someone views the photos and saves the images; they will not see anything because of the transparency.
would the code be common for Java and Android? I would like to prototype it in Java first.
I have found some code that combines two files. One file is my main file the other is a transparent file. The combined file does not have a transparent overlay.
Do I need to use an image drawing order?
http://www.developer.nokia.com/document/Java_Developers_Library_v2/GUID-D3E35E6F-0C45-48ED-B09D-F716E14C1C02/javax/microedition/amms/control/imageeffect/OverlayControl.html
BufferedImage image = ImageIO.read(new File("rose.jpg"));
BufferedImage overlay = ImageIO.read(new File("myimg1.gif"));
// create the new image, canvas size is the max. of both image sizes
int w = Math.max(image.getWidth(), overlay.getWidth());
int h = Math.max(image.getHeight(), overlay.getHeight());
BufferedImage combined = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
// paint both images, preserving the alpha channels
Graphics g = combined.getGraphics();
g.drawImage(image, 0, 0, null);
g.drawImage(overlay, 0, 0, null);
// Save as new image
ImageIO.write(combined, "PNG", new File("combined.png"));
This won't work. If they can see the image, they can copy it, one way or another. A solution to consider is providing watermarked thumbnails at no charge, then only offering the full resolution image for a fee. However, they can still copy the full resolution image once they pay.
You can actually put a trans image as an overlay to the orginial image, that will
Protect from download, I think this is usually done by the server side aka your cloud
I know from some websites that they use some kind of an overlay such as yours
And the browser can't see the image below so you can't download.
I actually didn't understand how you implementing this - the image is opened in the browser?
Just a wild though, you can also cut the image into pieces like a jigsaw puzzle
The device won't have problems connecting it togther but when you download you'll
Download only "one piece of the puzzle" :-P
I'm developing an app for BlackBerry with Eclipse and want to know if there is any way to save a picture that is drawn with a Graphics object...The app involves drawing a pie chart using Graphics.It works fine the first time I open the screen to display the chart but the next time I try to create the chart by calling the screen, it goes haywire.So I was wondering if I can save the chart as an image...that way I wont have to keep drawing over and over everytime I open the screen...Please help...Thanks in advance!
I understood you are using the java.awt.Graphics class, right?
Ensure you are drawing only on the AWT event thread.
Use
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// YOUR CODE
}
});
to run your saving code on the event thread. If you try to save from another thread, the image could get distorted if it is not drawn completely.
I don't know the blackberry API but you could take other aproach to your problem. You could do offscreen rendering. Create a BufferedImage, get a reference to its Graphics object, do the rendering and then save the BufferedImage as a png for example.
For example:
int width = 200, height = 200;
// TYPE_INT_ARGB specifies the image format: 8-bit RGBA packed
// into integer pixels
BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D ig2 = bi.createGraphics();
// Draw your chart
ImageIO.write(bi, "PNG", new File("yourImageName.PNG"));
This way you can do all your drawing once, saving it as a file or just in memory (depends on what you need) and then you just need either to load the image from the file or do g.drawImage() in your screen.
But as i said before i don't know if this applies to the Blackberry API it works for sure with the JDK/JRE in a desktop.