Double buffering in Java on Android with canvas and surfaceview - java

How does one go about doing this? Could somebody give me an outline?
From what I've found online, it seems like in my run() function:
create a bitmap
create a canvas and attach it to the bitmap
lockCanvas()
call draw(canvas) and draw bitmap into back buffer (how??)
unlockCanvasAndPost()
Is this correct? If so, could I get a bit of an explanation; what do these steps mean and how do I implement them? I've never programmed for Android before so I'm a real noob. And if it isn't correct, how DO I do this?

It's already double buffered, that's what the unlockCanvasAndPost() call does. There is no need to create a bitmap.

The steps from Android Developers Group say that you need a buffer-canvas, to which all the renders are drawn onto.
Bitmap buffCanvasBitmap;
Canvas buffCanvas;
// Creating bitmap with attaching it to the buffer-canvas, it means that all the changes // done with the canvas are captured into the attached bitmap
tempCanvasBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
tempCanvas = new Canvas();
tempCanvas.setBitmap(tempCanvasBitmap);
// and then you lock main canvas
canvas = getHolder().lockCanvas();
// draw everything you need into the buffer
tempCanvas.drawRect.... // and etc
// then you draw the attached bitmap into the main canvas
canvas.drawBitmap(tempCanvasBitmap, 0, 0, drawView.getPaint());
// then unlocking canvas to let it be drawn with main mechanisms
getHolder().unlockCanvasAndPost(canvas);
You are getting the main buffer, which you are drawing into without getting different double-buffer canvas' on each holder's lock.

Related

Android Canvas Graphics

I'm extremely new to Android development, but I have lots of experience with Java. What I'd like to know is how to independently draw on a View using a Canvas object.
What I want is an Android equivalent of this Java code:
BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB);
Graphics g = img.getGraphics();
g.drawImage(whateverImage, 0, 0, null);
g = frame.getGraphics(); /*just pretend 'frame' is a JFrame that we're using*/
g.drawImage(img, 0, 0, 100, 100, null);
Basically all that code does is make a blank bitmap image, draw on that image, then draw the bitmap onto a JFrame to display it.
I'm aware that Android doesn't use BufferedImage or Graphics, but I believe that the Android pseudo-equivalents are Bitmap and Canvas, respectively.
The problem is that when I have code that like this:..
Bitmap guy = BitmapFactory.decodeResource(getResources(), R.drawable.guy);
Canvas c = new Canvas(Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888));
c.drawBitmap(guy, 0, 0, null);
...I take it that the same concept of a blank bitmap being created and then drawn on occurs. However, I don't know how to then draw the Canvas onto a View, SurfaceView, or some other method that will display it on my device's screen.
It's important to note that I do not want to #Override onDraw and use it to draw my graphics, as I would like to control the drawing independently using some sort of thread loop.
If I did not provide enough information or my question is nonsensical, feel free to either ask me about it or ignore it. Any help is greatly appreciated.
Also, if anybody has any tips, tutorials, references, or anything else to better my understanding of layouts, views, etc. that would be greatly appreciated as well. Like I said, I am very inexperienced and trying to learn.
EDIT: Now working. Thanks :) https://i.stack.imgur.com/ugWlu.png
The only way to draw onto a View is to override onDraw. What you can do though is draw to an in memory bitmap (like you do here) in any other function, then draw the bitmap to the screen in onDraw. Just do
public void onDraw(Canvas canvas) {
canvas.drawBitmap(inMemoryBitmap, 0, 0, paint);
}

Alternatives to Canvas.snapshot() on JavaFX

I'm developing a little graphic engine using Canvas in JavaFX. In some point I had to render an off screen image, and then print it on my primary canvas using its GraphicContext.
I'm using this code right now:
private Canvas offScreenCanvas;
private GraphicsContext offScreenGraphic;
private SnapshotParameters parameters;
private WritableImage offScreenImage;
[...]
offScreenCanvas = new Canvas(WIDTH, HEIGHT);
offScreenGraphic = offScreenCanvas.getGraphicsContext2D();
parameters = new SnapshotParameters();
parameters.setFill(Color.TRANSPARENT);
[...]
offScreenImage = offScreenCanvas.snapshot(parameters, offScreenImage);
graphic.setGlobalBlendMode(BlendMode.HARD_LIGHT);
graphic.drawImage(offScreenImage, 0, 0);
graphic.setGlobalBlendMode(BlendMode.SRC_OVER);
My problem is the method snaphot() takes too much time, ~14ms, in each execution. I need to update the canvas at least at 60fps, so this consumes practically all the time I have to draw.
Is there another way to get an Image or WritableImage from a canvas? Maybe another different process?
This is another method to obtain a visual equivalent result, without reduce performance.
I have used java.awt clases, instead of JavaFX clases. The creation of a java.awt.image.BufferedImage offers the possibility to get a java.awt.Graphics2D where you can draw using other methods. The main problem here is that draw big images consumes a lot of time using this libraries, but you can create a scaled image. In my case, I have created a quarter-size BufferedImage, and I have drawn all the objects using that scale factor.
At the end of the draw process, just convert the BufferedImage, to a javafx.scene.image.Image, using:
SwingFXUtils.WritableImage toFXImage(BufferedImage bimg, WritableImage wimg);
Then print it on the main canvas using:
graphic.drawImage(Image image, 0, 0, WIDTH, HEIGHT);
To fill all the canvas with the image.
Finally, the scale factor is configurable, so if you need a detailed image, just use a higher value. For me, a 25-percent-size image is enough because I am drawing gradients. Now, it takes 1-3ms to draw the image, this is much better than before.
Hope it helps someone.

Scrolling BufferedImage Java

I'm making a simple side scroller game in Java but am having trouble making the camera move with the person. My plan was to create a buffered image that could adjust the region of the image that is displaying and simply have it follow my character. However, I couldn't find any functions in the API to change the region of the buffered image that's displayed, so how could I do this? Thanks for any help provided.
//The level is created in an 800x400 pixel size, is there any way I can change
//the region myImage displays?
myImage = new BufferedImage(400, 400, BufferedImage.TYPE_INT_RGB);
Well while you will be drawing your image you have a lot of options for example this function can draw any rectangle from your original image on any rectangle of the surface you get Graphics object from.

How to draw Canvas on Canvas

I`m building an android App, and i got stuck with a simple thing: How do i draw (or "add") a Canvas object, to another Canvas object, like "merging" them?
If that`s not possible, what is the best solution for doing that?
Thanks!
This depends entirely on your implementation.
If each Canvas draws objects directly from an array (of shapes, etc.) each frame, you could simply append one array to the other. This way, your Canvas does not need to be drastically altered, it only has to add one array to another (possibly an ArrayList would be the way to go here).
If the above is not the case, you may have to make some more drastic changes. When I encountered a similar problem, I created a new method called commitChanges(), which added a series of changes to an existing Canvas (adding lines on top, etc.).
I first invalidated the affected area, then created a Bitmap with the size of the Canvas: Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.RGBA_8888);.
Next, I created a canvas from that Bitmap: Canvas workingDrawing = new Canvas(bmp);.
Then, I drew everything I needed onto that new Canvas. In this case, that would be the data from one of your Canvases.
Now, in your other Canvas, you have to get the Bitmap you just drew, then draw it onto this Canvas. Like so: canvas.drawBitmap(yourDrawnBitmap, 0.0f, 0.0f, null);.
I think the difficulty you'll face is transferring the data from one Canvas to another. But, regardless of your implementation, one of the above methods should work effectively for you.

How to obtain the current bitmap of a canvas?

I want to get the current bitmap associated with my canvas so I can perform operations on it. I can't see how to do this though.
I've seen some examples where you create a bitmap and set the canvas to use this bitmap, so obviously you can then access it later, but I'm using the canvas returned from a SurfaceHolder so there's no constructor.
For instance, examples often show this kind of thing:
Bitmap bmp = Bitmap.createBitmap(xxx, yyy, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(bmp);
so at this point I can see bmp.
In my case, the canvas is obtained by:
final SurfaceHolder holder = getSurfaceHolder();
Canvas c = null;
try {
c = holder.lockCanvas();
So how can I get the bitmap for c?
Edit
#Reuben - you may be right, I did wonder this. In short, my aim is to capture the current canvas contents where I have drawn "stuff", and make a copy of it, reversed, to put underneath. Like a reflection. The example of this that I found performed it all via bitmaps, so I assumed I needed to somehow capture the current canvas to a bitmap to then use it. If there is a better way I can do this, I'm all ears!
Better late than never :)
BitmapDrawable bitDrawable = new BitmapDrawable();
bitmapDrawable.draw(videoView.getHolder().lockCanvas());
You can then access the bitmap back from the BitmapDrawable.
in my case i did this:
imageView.setImageBitmap(bitmapDrawable.getBitmap());
c.getSurface() gives you direct access to the Surface object.

Categories