I'm looking for a fast and easy way to plot arbitrarily colored pixels in an SWT Canvas.
So far I'm using something like that:
// initialization:
GC gc = new GC(canvas);
// inside the drawing loop:
Color cc = new Color(display, r, g, b);
gc.setForeground(cc);
gc.drawPoint(x, y);
cc.dispose();
This is horribly horribly slow. it takes about a second and a half to fill a 300x300 canvas with pixels.
I could create an image off-screen, set the pixels in it and then draw the image. This will be faster but I specifically want the gradual painting effect of plotting the image pixel by pixel on the canvas.
I bet that what is killing performance is allocating and releasing 90,000 Color objects. Remember, in SWT, each Color object allocates native resources, which is why you have to dispose() it. This means each time you allocate and dispose a Color object, you have to transition from the JVM to native code and back.
Can you cache your Color instances while in the 300x300 pixel loop and then dispose of the objects after your loop? You'd need a somewhat intelligent cache that only holds a maximum of so many objects, and after that will dispose of some of its entries, but this should speed things up greatly.
You could draw several offscreen images where you gradually fill the 300x300 area. This way you can control how fast the image should appear.
Create a BufferedImage object:
BufferedImage bi = new new BufferedImage(300, 300, BufferedImage.TYPE_INT_RGB);
inside the drawing loop set your pixels:
bi.setRGB(x, y, int_rgb);
...
and finally display the buffered image:
g.drawImage(bi, 0, 0, null);
If you find setRGB() slow, you can access the bitmap data directly:
int[] raster = ((DataBufferInt)bi.getRaster().getDataBuffer()).getData();
and later
raster[y * 300 + x] = int_rgb;
Related
I'm making a game where you start by drawing your map. At the moment it works by having an Area instance variable and then when the player clicks/drags the mouse it adds Ellipse2Ds to it. Here's what I mean:
Area land = new Area();
And then in the MouseDragged method
Point2D mouse = e.getPoint();
Ellipse2D ter = new Ellipse2D.Double(mouse.getX() - drawRad, mouse.getY() - drawRad, drawRad*2, drawRad*2);
land.add(new Area(ter));
And then basically the same except land.subtract(new Area(ter)) for erasing.
My problem with this is that after doing a lot of drawing it becomes very slow to draw the Area. the other problem is that I would like to be able to easily get the outline of the drawing, and I haven't found a good way of doing that using Areas. Using area.getPathIterator is not nearly accurate enough.
So I'm wondering what other ways of saving drawings are? I can't just have an array of Ellipses because then erasing wouldn't work.
Thanks!
If you are drawing the same thing over and over it may be worth while to draw it to an image once or load it from a file at start up and then just paint the image rather than painting all the individual shapes.
To load from a file, put your picture file in the same directory as your .java file and load:
BufferedImage img = ImageIO.read(this.getClass().getResource("picture.png"));
To draw on the image once:
BufferedImage img = new BufferedImage(width,height, BufferedImage.TYPE_4BYTE_ABGR);
Graphics2D g = img.createGraphics();
In your paintComponent method you draw the image somewhere.
g.drawImage(img, 0, 0, this);
If you have something moving over a background, instead of adding and subtracting the moving item. Just draw the background and then draw the thing that moves, drawing the background will effectively erase the old position with less computation.
I'm making a small game in Java, and it uses a pixel-graphics style, with many sprites 16x16 or 32x32 pixels. However, when I rotate them, I do not want "jaggies" along the side, so I used the RenderingHint
RenderingHint.KEY_INTERPOLATION
RenderingHint.VALUE_INTERPOLATION_BILINEAR
Unfortunately, because many of the images used are very small (16x16, 32x32) the resulting image is completely unusable. Output:
http://imgur.com/a/roRh4
As you can see, the small graphics are blurred. This is the intended effect for large images, but for small images, it is very bad.
One solution is to increase the resolution of all my graphics, while keeping the "blocky" effect. That would be a pain, so is there another way to tweak the interpolation?
Thank you guys so much.
Upscale your tiny images and use that instead:
BufferedImage tiny = ImageIO.read(new File("..."));
BufferedImage bigger = new BufferedImage(tiny.getWidth()*10, tiny.getHeight()*10, tiny.getType());
Graphics2D g = bigger.createGraphics();
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
g.drawImage(tiny, 0, 0, bigger.getWidth(), bigger.getHeight(), 0, 0, tiny.getWidth(), tiny.getHeight(), null);
Replace the 10 scaling factor by the smallest value that gives acceptable results.
Do the rest of your rendering with high quality interpolation.
I'm trying to change the transparency of an image over time, and I'm doing this with the method drawImage() from java.awt.Graphics. I know there's a lot of different answers online about how to do this, but I can't find a one that is simple enough for me to understand and implement.
Let's just say I have a BufferedImage image, and I want to draw this image with a 50% opacity. How would I initialize image and what would I do to adjust the alpha level of the image when I draw it. It would be great if I could use the method drawImage() and do something with that to change the transparency of the image, but it's probably not that simple.
Never tried it, but I think the basic code would be:
AlphaComposite ac = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha);
g2d.setComposite(ac);
g2d.drawImage(...);
Using image filter.
float[] scales = { 1f, 1f, 1f, 0.1f };
float[] offsets = new float[4];
RescaleOp rop = new RescaleOp(scales, offsets, null);
g2d.drawImage(buffimg, rop, 0, 0);
4th element in scales array is transparency, this value goes between 0 - 1
Answer by camickr will make the entire component apply the alpha including all inner components. But that will be much faster.
Warning: Use Image Filters with Care
ref: http://www.informit.com/articles/article.aspx?p=1013851&seqNum=2
I am using GC (org.eclipse.swt.graphics.GC) to combine some pictures into one image (using drawImage). The problem is that, by default GC has got white background. Is it possible to set background to transparent? Or any other solution to cut part from one picture and paste into second?
Simply, GC is a pen, pen can't control canvas's transparency. It just can only draws something on canvas. So, GC can't manipulate image itself, just GC can draw something on Image.
However, You can directly control transparency data with ImageData API.
You should have to know what there is no advanced drawing API except setPixel(x, y, pixel), setAlpha(x, y, alpha) on ImageData.
You could use a Canvas with no background
Canvas canvas = new Canvas(parent, SWT.NO_REDRAW_RESIZE | SWT.NO_BACKGROUND);
And then you could (like in this example) make one(or more) colors of the image transparent.
ImageData ideaData = new ImageData(getClass().getResourceAsStream("Idea.jpg"));
int whitePixel = ideaData.palette.getPixel(new RGB(255,255,255));
ideaData.transparentPixel = whitePixel;
Image transparentIdeaImage = new Image(display,ideaData);
We have an old Java Swing application. we need to display thousands, hundreds of thousands small circle spots on the canvas based on the real data. Right now we have an image file of a small circle spot. When we need it, we draw that image onto the canvas, thousands, hundreds of thousands times.
Now I am think it may be better (better performance and memory usage) to just draw a filled circle each time instead of load the image and draw it.
how about your opinion?
thanks,
You only need to load the template image once and hold it in memory and copy it to the canvas as needed using Graphics2D drawImage function. Drawing multiple filled circles may become expensive due to calls to the Flood-fill/Scan-fill algorithm as well as Bresenham to draw the circle. To optimize the rendering you can also decimate the rendered result or perform clustering, since the user will not really appreciate dense overlapping circles anyway.
To reduce render calls test the pixel where your template is going and pass a render if it is already coloured.
Here is a nice benchmarking applet.
It is almost certainly much faster to hold a single image and draw it many times than to make a call to draw a filled circle. Here is a recent presentation on the subject, showing that it is faster to draw an image than even a simple horizontal cross. http://developers.sun.com/learning/javaoneonline/j1sessn.jsp?sessn=TS-4170&yr=2009&track=javase
Time your code
It is most definitely faster to draw an image lots of times than drawing a circle or String lots of times and it's very easy to test. At the beginning of your paintComponent() method add the line:
paintComponent(){
long start = System.currentTimeMillis();
...
// draw 100,000 circles as images or circles
...
System.out.println("Rendering time: " +
(start - System.currentTimeMillis()) + " ms");
}
If the times turn out to be zero all the time, you can instead use System.nanoTime().
Paint to Cached Image
Another thing you can do is to paint these circles onto an image and only recreate the image when the content changes. If nothing has changed just draw that image onto the Graphics2D object instead of redrawing all of the circles. This is commonly called double buffering. You also can use Volatile Images to take advantage of hardware acceleration.
Create Compatible Images
You should also make sure you use images that are compatible with the user's monitor by using createCompatibleImage() as shown below:
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice gs = ge.getDefaultScreenDevice();
GraphicsConfiguration gc = gs.getDefaultConfiguration();
// Create an image that does not support transparency
BufferedImage bimage = gc.createCompatibleImage(width, height, Transparency.OPAQUE);
// Create an image that supports transparent pixels
bimage = gc.createCompatibleImage(width, height, Transparency.BITMASK);
// Create an image that supports arbitrary levels of transparency
bimage = gc.createCompatibleImage(width, height, Transparency.TRANSLUCENT);
More Tips
I'd recommend the book Filthy Rich Clients. It has lots of great tips for speeding up swing. Especially look at chapters 4 and 5 about images and performance.
I don't now if this would be helpful but you can test which one works for you by testing worst case . But I think filled circle would be best .
A third way to do it is to use the unicode char for filled circle, ●, since you can bet that rendering thousands of chars (as in: a piece of text) is the most normal thing for any graphics engine.
It's hard to predict which is faster, because certain operations under certain circumstances are accelerated by the GPU hardware of the video card.
If the GPU is used to make the circle, then that would be much faster than the cpu copying pixels of a buffered circle as an image.
There is VolatileImage as well. Perhaps it's possible to make the image blits so that they end up being accelerated.
The only way to find out is to benchmark it yourself.