I have stumbled upon a bottle neck that I would love to fix.
I need to make a BufferedImage grow with time, but the Buffered image should support scrolling as my application requires that the Bufferedimage grow a significant size over time. However the draw calls are not done by the event dispatch thread.
I have a while loop that performs the render calls. What I attempted was to create an instance of a Canvas and add it to a JScrollPane however when I take this approach, the JScrollPane performs its own draw calls and I am not sure how the JScrollPane will detect that the canvas has resized at runtime.
The other issue is that since within the canvas I use a BufferedImage to draw onto, I cannot get it to resize with a temporary BufferedImage.
Here is how I attempted to create a new, larger BufferedImage
if(needsToBeResized)
{
BufferedImage temporaryBufferedImag =
new BufferedImage(originalImage + extraSpace, height, originalImage.getType());
Graphics g2d = temporaryBufferedImag.createGraphics();
g2d.drawImage(originalImage, 0,0,null);
//I presume that this should copy the graphics object with the original img
originalImage = temporaryBufferedImag;
g2d.dispose();
}
If it helps I am developing an oscilloscope type application that needs to be able to keep plotting values real-time but I also need to be able to show the history of values.
Render each interval in its own "mini" BufferedImage. So, for a gross example, say you render every second, and you want to show 20 seconds of history.
So, you render your current second in to a "1 second sized" image. Then you take your cache of the images from the previous 19 seconds, and you stamp them all together, one after the other in to your new total image, and display that.
Next second, you drop off the oldest image, create a new one, stick it on the end, rinse and repeat.
If your overall frame size changes, when you get to render all of the pieces all over again…c'est la vie.
Obviously you will be sampling more than once a second, but you get the gist of it.
All this assumes this is cheaper than simply re-rendering the entire thing from scratch each time.
Related
I don't think I understand how the render method works.
From what I read online / saw on youtube tutorials, I gathered that the render method is a looping function that you can use to keep your game updated and read users' inputs. When you want to draw something in the screen you do so in this method, and before drawing anything you clear the screen.
But if I want to draw something stationary, wouldn't it be better to draw it outside the render method (so the computer doesn't have to clear the screen and redraw everything many times)?
What am I missing?
You can't draw outside the render method, because it's the method called on the OpenGL thread. You can't draw to OpenGL from other threads.
If your entire screen is frequently static for more than a few ms, which might be the case in something like a card game, you can disable the continuous refreshing of the screen using Gdx.graphics.setContinuousRendering(false). This will save energy because the GPU and CPU won't have to work as hard. See here for instructions on using it.
As for why this typically isn't done:
Think of a "frame buffer" as a bitmap image in memory that can be drawn to and then shown on screen. Most mobile rendering is done with double-buffering. This means while one buffer is being drawn to (the back buffer), the front buffer that was drawn to on the previous frame is being rendered directly to the screen. They swap back and forth each frame for faster rendering.
The way graphics work is that each item that's drawn modifies the color of pixels in the frame buffer where the item is drawn. Items may be masked or be translucent, and these will alter the color of pixels behind them. The original color of those pixels are lost once something is drawn in front and modifies them
So, even if some item on your screen is static, if there are other items on the screen that move or change color, and they overlap the static item (either in front of or behind), you still have to redraw the whole screen every time anything changes.
If one area of the screen is static and never overlapped by dynamic objects, and the dynamic objects are contained entirely within a rectangle, you could potentially set a viewport that limits drawing to the dynamic area and avoid clearing the whole screen. However, GPUs are designed specifically to be efficient at clearing the whole screen. Because of double-buffering, if you don't clear the whole screen, it's actually likely to harm performance because if you don't clear it, the contents of the screen have to be copied between the front and back buffers before you can start drawing the next frame.
Another strategy is if you have a static object that is very complicated and taxing for the GPU to draw because it has lots of layers or a complicated shader, you could draw it to an off-screen frame buffer object (FrameBuffer class in libGDX) one time, and then render that FrameBuffer's texture to the screen on each frame like a sprite.
I am working on a java game which deals with a bunch of sprite sheets, and I was wondering whether I should have separate sprite sheets for left and right animations, or if I should just draw up the left sprites and reverse the image programatically for the right animations. Which one would be a better practice, and would either of them perform better? I was thinking of having the image flipping occur during Game init(). If I do go with direction flipping (saving a lot of time in photoshop), would this be a safe way to go:
playerAttackLeft = spriteSheet.crop(0, 0, 400, 400); //(x, y, width, height)
playerAttackRight = spriteSheet.crop(400, 0, -400, 400);
?
You should rotate the image and use it instead of getting new one.
When you read an image then it will take space for JVM to load it.
Here is an example when I did it on my computer.
I had an image of 100kb and when I loaded it in my class, It has taken approximately 1mb of space.
reading an image is costly process
And on the other hand if you will use rotated image it will not only save your space but also your time too (space and time complexity, both) because rotating image will take much less time then to read an external image.
I've read that JPanel has a double buffering system, but I've also seen people manually create their own back buffer using BufferedImage, which allows the user to add to the image before actually rendering.
How does the JPanel double buffering work, and why would someone create their own back buffer if JPanel already has double buffering? What is an example of a time where BufferedImage as your buffer would be needed/recommended?
From what I've seen it's usually because:
They are already loading an image which serves as a background image and wish to draw on top of that (they probably could just draw the image, leaving it to be hardware accelerated for future renders while drawing everything else using the same Graphics object used to call drawImage)
Because they wish to have a simple way to scale their graphics as their panel resizes with drawImage(Image, 0, 0, panelWidth, panelHeight, ImageObserver) and don't wish to do the extra work on calculating dynamic drawing coordinates for the graphics based on the panel's current dimensions (although this leads to not so good looking graphics, and if the scaled image is not cached per resize there could be performance issues as well).
Because it's old code in the days before Swing was double buffered, or people following code from old books.
The graphics of what you draw in the double buffered painting methods (like paintComponent(Graphics)) will be visible all at once when all of the relevant painting methods are complete. That being the case there's no reason to use a BufferedImage as a way to manually ensure all the Graphics get shown at once.
I am making a grid-based game that resizes its grid as the window size changes. I also may apply color filters for lighting effects in the future. I am concerned about the performance of this code, which draws to the screen an image in one of the grid squares.
public void drawSquares(Graphics g){
ListIterator<Viewport> iterator = vp.listIterator();
while(iterator.hasNext()){
Viewport v = (Viewport)iterator.next();
BufferedImage img = v.getSqView().getImage();
Rectangle b = v.getPixRect();
g.drawImage(img, b.x, b.y, b.width, b.height, v.getSqView().getBackground(), null);
}
return;
}
What this code does is get the image (stored in img) and get the pixel rectangle it needs to fit in (stored in b), then draw it in the space alloted via drawImage.
drawImage says that it scales images on the fly - which means that all images are being rescaled every frame. But the window is only resized rarely, so this must waste lots of processor time doing the same thing over and over again.
Now I saw this and decided that I would just update all the images upon resizing once, then store the result and be able to draw normally.
Like this:
public void resizeImage(int width, int height){
BufferedImage resized = new BufferedImage(width, height, img.getType());
Graphics2D g = resized.createGraphics();
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g.drawImage(img, 0, 0, width, height, 0, 0, img.getWidth(), img.getHeight(), null);
g.dispose();
img = resized;
}
This doesn't work - I think it has something to do with img = resized. I just lose all the images with this code.
I have a few questions then.
What is the performance cost of repeatedly scaling with drawImage? Is it any different even if the window has not been resized in between frames?
How should I get the second code snippet to work? What is going wrong?
If I apply a lighting filter to a tile, will that eat up tons of processor time as well if I run it each frame? (Think 225 or so small images on a 800x800 or so display)
What is best practice for applying lighting filters? I am planning on overlaying on the whole map a pitch black filter, then exposing the areas around light sources.
Thanks for any help with this!
Resize the frame of this Grid to get a subjective feel for the latency. Use the approach shown here to measure the latency. Verify your findings in a profiler.
There's no reason you shouldn't be able to resize the elements of a List<Image> as you propose, but add() the resized instances to a new list as they are created.
What is the performance cost of repeatedly scaling with drawImage? Is
it any different even if the window has not been resized in between
frames?
You should always measure, but there is definitely a performance cost here, even if the window is not resized, because as the Javadoc says, there is no caching behind this drawImage method. The cost also depends on the frame rate.
How should I get the second code snippet to work? What is going wrong?
The second code snippet should be OK, I think the problem is somewhere else. Try reproducing the problem in a "small but complete" program, and post another question if you still see the problem.
If I apply a lighting filter to a tile, will that eat up tons of processor time as well if I run it each frame? (Think 225 or so small images on a 800x800 or so display)
You should always measure :)
What is best practice for applying lighting filters? I am planning on overlaying on the whole map a pitch black filter, then exposing the areas around light sources.
You can use an AlphaComposite for this.
I'm not quite sure how to phrase this, so bear with me.
I have two JPanels in a container JPanel with an OverlayLayout. Both JPanels in the container override paint(Graphics).
The bottom JPanel is opaque and draws some fairly complicated graphics, so it takes a "long" time (10s or 100s of milliseconds) to render.
The top JPanel is transparent and just draws a rectangle or line or simple shape based on mouse input, so it's really quick.
Is there a way to set things up so when I change the simple shape in the upper panel, it doesn't redraw the bottom panel? (e.g. it somehow caches the bottom panel)
I'm vaguely familiar w/ concepts like bitblt, double-buffering, and XOR-drawing but not really sure what to apply here.
You'd be best off using a single JComponent and creating a BufferedImage to store the bottom image. When the paintComponent operation happens on the JComponent, you just blit the bottom image and use the Graphics object to do any further drawing on top of that (from a stored state). Should be fairly efficient.
You'll want to do the complex drawing operations for the bottom BufferedImage in another thread, as the other poster mentions (omitted this by accident, sorry :)). However, you don't want to cause contention on this image, so you must store an additional BufferedImage for this, and blit it synchronously to the other image at the very moment the drawing operations are complete on it.
Focusing on the complicated panel, the key is factoring everything out of paintComponent() except drawImage(). Put everything else in another thread that continually updates an offscreen buffer. Periodically update the screen at some rate that keeps the simple panel responsive. The only hard part is synchronizing, but SwingWorker is a good choice. There's more here.
What's sure is that if the upper panel is target for a full repaint(), then the lower one will be also.
Maybe you can try to optimize the region to repaint on the upper panel in order to avoid repainting all the lower one. But if the painted rectangle in the upper panel covers the whole area, then you end up with full repaint() once again.
Normally, Swing tries to optimize the regions that need a repaint, but it also aggregates these regions when several repaint are performed in a short time, and if I remember well, the aggregated region is just a rectangle that is the union of all repaint rectangles, which is not always optimized but allows for fast computation of repaint events creation.
Now, I think you should follow the advices given in previous replies; indeed, you should really avoid having a paint() method that can perform computations that can be that long (a few 10s of ms should be the real maximum). Painting should be as fast as possible if you don't want to have a GUI that looks unresponsive to the end user. Hence, favour performing the computation only once (and outside the EDT if possible) store the result in a BufferedImage that you just simply draw later on in the paint() method.
EDIT: added other sources of reflection
If you want to optimize the update of the list of points but still keep it in the paint() method, then you can use the clipping region of the passed Graphics to limit the calls to drawing methods, something like:
Rectangle clip = g.getClipBounds();
for (Point p: allPoints) {
if (clip.contains(p)) {
// draw p with g graphics
}
}
You can even try to optimize the list of points to draw by using a QuadTree instead of a simple List, but you'll have to code it yourself (or find some free implementations, there are probably a few of them out there). With a quadtree, you can optimize the time to find the list of all points that have to be redrawn (based on the Graphics clipping rectangle) and only redraw those points.
Addenda for answer by trashgod and jfpoilpret
1/ OverlayLayout is strange way how to layout JPanels, are you same output with once JPanel (without OverlayLayout and Translucentcy)
2/ (10s or 100s of milliseconds) is maybe small value because there is Native OS Latency (45-75ms for today OS and PC)
3/ synchronizations would be managed by using SwingWorker on BackGround Task and with order, directions and synchronizations for painting processes to the JPanel, maybe your paints are too fast/quickly
4/ you didn't describe more about how, where and which about paint()/paintComponent()
if (SwingUtilities.isEventDispatchThread()) {
paintImmediately(int x, int y, int w, int h) // or Rectangle r
} else {
Runnable doRun = new Runnable() {
#Override
public void run() {
repaint(long tm, int x, int y, int width, int height) // or Rectangle r
}
};
SwingUtilities.invokeLater(doRun);
}