Please consider the following sample image :
All the objects (rectangles, shapes, texts, etc..) are written in BufferedImage. My questions is, after I write a graphics.drawline(..) on top of them, how to undo, or reset or clear the line(s) I created. Even if I re-execute the initialization of my graph, the lines that I drew are still there.
I can still capture the coordinates of the lines. If it is a plain background I can just re-draw it with the same background. But, in this case, this wont work.
There is no way to undo things that you do with Graphics. But there are other things you can do instead with which you can get your image back.
Keep a copy of image before you do anything which you might want to undo.
And redraw it when required.
Keep a clip image of the area of image before you do anything which you might want to undo. And redraw that area when required.
To keep a Stack of these images of limited size will be a good idea to undo back in series. Don't make the stack size too big because it will consume your heap memory
I found an answer using deep copy :)
So, first, I deep copied the original or default image. Then, If I write something on the original I overwrite it with the back up copied (deep copied again)..
static BufferedImage deepCopy(BufferedImage bi) {
ColorModel cm = bi.getColorModel();
boolean isAlphaPremultiplied = cm.isAlphaPremultiplied();
WritableRaster raster = bi.copyData(null);
return new BufferedImage(cm, raster, isAlphaPremultiplied, null);
}
Complete code can be found here..
Related
I've the known problem that my bitmap/canvas is too big and it throws the java.lang.OutOfMemoryError.
My question is what would be the best for my needs.
The Canvas should draw a graph (with given points) and can be very wide (like 3000px and more, theoretically it's wide could be much more, like 20000px). The height is fix.
Because thats to wide for any screen I put it in a Scrollview and draw the whole graph into the canvas.
So thats to wide for the bitmap and I get the error.
The second possibility would be a fix sized canvas where I'd write a "onScroll" method that redraws the graph depending on the users swipe. So it'd only draw a part of the graph.
Would that be the better way or is there a way to make the first option work?
Anyhow please give me some hints and example code for the solution.
Here is the code:
Bitmap bitmap = Bitmap.createBitmap(speedCanvasWidth,speedCanvasHeight,Bitmap.Config.RGB_565); //I also tried ARGB_8888
speedCanvas = new Canvas(bitmap);
graph.setImageBitmap(bitmap);
Thanks in advance
You can handle this with a BitmapRegionDecoder. Just create an instance of one that points to your image. The system will maintain a handle on the image and then you can call decode on the decoder based on what rectangle you want to be displayed within the canvas. Updates to the canvas will have to be handled based on your needs. This will help prevent loading this large image that you have to handle.
You can further get details of the Bitmap in question by checking the Bitmap information. This can be done by loading the bitmap into memory with BitmapFactory.Options flags set for to true for inJustDecodeBounds. That keeps the Bitmap from actually being loaded into memory during the checks.
For instance, a quick retrieval could be done with the following:
BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance("pathToFile", true);
Bitmap regionOfInterestBitmap = decoder.decodeRegion(rectWithinImage, null);//Or with options you have decided to load.
I am trying to write a SWT component, that is able to take and draw an instance of java.awt.BufferedImage. My problem is that SWT's Image and AWT's BufferedImage are incompatible: SWT components can't draw java.awt.Image, AWT/Swing components can't draw org.eclipse.swt.graphics.Image.
There are several approaches that try to solve this problem in other ways (which also may have some variations, but basically there are these two):
Convert between SWT Image and AWT BufferedImage
Swing/SWT Integration
They all have shortcomings and didn't satisfy my expectations:
The first approach, to convert an SWT Image to a BufferedImage, results in poor performance for large images due to the creation of a new RGB instance for every Pixel.
The second approach has several shortcomings in usability. See the "workarounds" at the end of the linked article.
This lead to the conclusion that I'd try my best to write a component (based on org.eclipse.swt.widgets.Canvas or org.eclipse.swt.widgets.Composite) which allows to draw a BufferedImage directly without any conversion of images.
My approach was to draw it pixel by pixel. Therefore I simply had to get an instance of GC, walk the source BufferedImage line by line, left-to-right and drawing the corresponding Color using GC.setForeground(Color color) and GC.drawPoint(int x, int y).
First, I created a new instance of Color for every pixel, which uses quite a lot of memory and adds an additional delay, since new Color acquires system resources and creating a new object for every pixel also takes its time.
Then I tried to pre-load all possible (24 bit) Colors into an array before drawing the image. This lead to an explosion of memory usage (>= 600 MB), which was clear before I was trying it, but I had to verify it.
Caching only the used Colors also lead to more memory consumption than would have been required.
I think there has to be a more low-level approach that doesn't require that much memory, since SWT is able to draw whole (SWT) Images without consuming that much memory.
I would appreciate any ideas or solutions.
I found out there's a way to "convert" an BufferedImage to an Image by using the original image's data buffer if it is 24 bit RGB. This is possible, since the image formats are compatible.
final BufferedImage original = ImageIO.read(new File("some-image.jpg");
final PaletteData palette =
new PaletteData(0x0000FF, 0x00FF00, 0xFF0000);
// the last argument contains the byte[] with the image data
final ImageData data = new ImageData(original.getWidth(), original.getHeight(),
24, palette, 4,
((DataBufferByte) original.getData().getDataBuffer()).getData());
final Image converted = new Image(getDevice(), data);
This way, one doesn't have to create thousands of new objects. This approach comes with the disadvantage that one needs to ensure that the original image is of type RGB 24 bit. Otherwise the image has to be converted to this format.
After that, an image can be drawn with the following code:
// get the GC of your component
gc.drawImage(image, 0, 0);
Probably other bit depths can be converted in a similar way, but this is all I need for the moment.
I am trying to rotate a bufferedImage of a missile turret so that it looks like it's following a target. Basically, I can do it easily with the AffineTransform/ affinetransform
my current code in a nutshell is:
public BufferedImage tower = null;
try
{
tower = ImageIO.read(SpriteSheet.class.getResource("/spriteSheet/testTower.png"));
}
catch(IOException e)
{
AffineTransform tx = AffineTransform.getRotateInstance(rotationRequired, locationX, locationY);
AffineTransformOp = op = new AffineTransformOp(tx, AffineTransformOp.TYPE_BILINEAR);
//then I draw it using
g.drawImage(op.filter(tower, null), towerLocationX, towerLocationY, null);
this works, but what I want to do is transform(rotate) the bufferedImage, then copy the newly rotated pixel data into a pixel array and then draw it onto the screen because I believe this is how most games draw rotating images as opposed to drawing a png directly to the screen.
But what do I know. How exactly do 2D games draw rotating images? Am I doing it correctly, or is there a better/ more memory efficient way of doing this?
There are a lot of ways to tackle image manipulation in 2D games. Before optimizing though, you should ask yourself if there's a real need for it to begin with. Moreover, memory optimization usually comes at the cost of CPU performance and vice verse.
If CPU time is the problem, a common approach is to keep an array of images already rotated to certain angles (precalculated).
If memory is the problem, keep a single image and calculate the rotated form each time it's displayed. An even more memory efficient yet CPU consuming approach, is to draw vector shapes rather than images. This also leads to better looking results than the interpolation of the smoothing algorithm used for images when transformed. Java supports SVG, and there are several good packages available (e.g. http://xmlgraphics.apache.org/batik/).
Finally, Java can be connected to graphic libraries in order to perform the rendering, thus improving performance. Such libraries (OpenGL, etc.) use the memory of the graphic cards to store images in order to improve CPU usage (http://jogamp.org/jogl/www/).
I'm working on a painting application using the LibGDX framework, and I am using their FrameBuffer class to merge what the user draws onto a solid texture, which is what they see as their drawing. That aspect is working just fine, however, the area the user can draw on isn't always going to be the same size, and I am having trouble getting it to display properly on resolutions other than that of the entire window.
I have tested this very extensively, and what seems to be happening is the FrameBuffer is creating the texture at the same resolution as the window itself, and then simply stretching or shrinking it to fit the actual area it is meant to be in, which is a very unpleasant effect for any drawing larger or smaller than the window.
I have verified, at every single step of my process, that I am never doing any of this stretching myself, and that everything is being drawn how and where it should, with the right dimensions and locations. I've also looked into the FrameBuffer class itself to try and find the answer, but strangely found nothing in there either, but, given all of the testing I've done, it seems to be the only possible place for this issue to be created somehow.
I am simply completely out of ideas, having spent a considerable amount of time trying to troubleshoot this problem.
Thank you so much Synthetik for finding the core issue. Here is the proper way to fix this situation that you elude to. (I think!)
The way to make frame buffer produce a correct ratio and scale texture regardless of actual device window size is to set the projection matrix to the size required like so :
SpriteBatch batch = new SpriteBatch();
Matrix4 matrix = new Matrix4();
matrix.setToOrtho2D(0, 0, 480,800); // here is the actual size you want
batch.setProjectionMatrix(matrix);
I believe I've solved my problem, and I will give a very brief overview of what the problem is.
Basically, the cause of this issue lies within the SpriteBatch class. Specifically, assuming I am not using an outdated version of the class, the problem lies on line 181, where the projection matrix is set. The line :
projectionMatrix.setToOrtho2D(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
This is causing everything that is drawn to, essentially, be drawn at the scale of the window/screen and then stretched to fit where it needs to afterwards. I am not sure if there is a more "proper" way to handle this, but I simply created another method within the SpriteBatch class that allows me to call this method again with my own dimensions, and call that when necessary. Note that it isn't required on every draw or anything like that, only once, or any time the dimensions may change.
I am wondering is there a way to convert Image to BufferedImage without code like a
new BufferedImage(...)
because every new init makes app run slower , moreover, if it is in paint() method :(
Please advise the most optimal conversion way.
Thanks
No. Not unless the original Image happens to be a BufferedImage already. Then you can just do a cast:
BufferedImage bufImg = null;
if (origImage instanceof BufferedImage) {
bufImg = (BufferedImage) origImage;
else {
bugImg = new BufferedImage(...);
// proper initialization
}
If it's not a BufferedImage it may very well be for instance a VolatileImage (the other concrete subclass in the API).
From the docs on volatile image:
VolatileImage is an image which can lose its contents at any time due to circumstances beyond the control of the application (e.g., situations caused by the operating system or by other applications).
As you may understand, such image can not provide the same interface as a BufferedImage, thus the only way to get hold of a BufferedImage is to create one, and draw the original image on top of it.
because every new init makes app run slower
Cache one BufferedImage, then only create a new image if the required size changes. Otherwise clear the Graphics object of the current instance and do whatever new drawing is needed.
Is there a way to draw a BufferedImage to JLabel with the paint() method?
One convenient approach is to implement the Icon interface. In this example, Histogram simply draws itself when the label is told to repaint().
If the source of the image requires a time-consuming operation such as scaling, pre-render the image as shown in the static factory, GradientImage.