How to obtain the current bitmap of a canvas? - java

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.

Related

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.

Magnetic sensor and image rotation in Android

trying to build an magnetic compass, I have the following code.
public void onSensorChanged(SensorEvent event) {
imageCompass = (ImageView) findViewById(R.id.imageMapDrawView);
Bitmap myImg = BitmapFactory.decodeResource(getResources(), R.drawable.compass);
Matrix matrix = new Matrix();
matrix.postRotate(event.values[0] );
Bitmap rotated = Bitmap.createBitmap(myImg, 0, 0, myImg.getWidth(), myImg.getHeight(),
matrix, true);
imageCompass.setImageBitmap(rotated);
}
I have the following questions,
A. I guess event.values[0] is in degree? not in radian?
B. I want to get the image from image view and rotate it around the center of the image, where I am telling that?
C. I want to draw another image (an indicator on top of that Image), can I do this? I have already an compass image in the image view and I want to draw another image on top. I can't redraw whole view. How can I achieve it?
A. Check http://developer.android.com/reference/android/hardware/SensorEvent.html.
The length and contents of the values array depends on which sensor type is being monitored.
Sensor.TYPE_ACCELEROMETER is in m/s²
Sensor.TYPE_MAGNETIC_FIELD is in uT (micro Tesla)
Sensor.TYPE_GYROSCOPE is in rad/s
and go on for all sensors (check the link).
Also, interface SensorListener is Deprecated since API level 3. You should use SensorEventListener instead.
B and C. You should consider using OpenGL ES for drawing your images. Specially OpenGL ES 2.0 gives you so much details and ways manipulate your images that would be easy to do let you do about anything you need.

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.

Double buffering in Java on Android with canvas and surfaceview

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.

Draw segments of a bitmap using Canvas

I have a picture (resource) that I would like to use for my application in Android. But I only want to draw specific segments of it. My initial thought is to turn it into a bitmap and specify which pixels need to be drawn and where. I tried canvas.drawBitmap(bitmap, src, dst, null); but it doesn't seem to work. Maybe I am not using it right.
Just wondering if it is possible at all, and what can I use to achieve this?
Thanks!
src = new Rect(20,40,20,40);
dst = new Rect(20,40,20,40);
canvas.drawBitmap(background, offset, 0, null);
canvas.drawBitmap(bitmap, src, dst, null);
I was hoping to see the area specified at src's coordinates to be drawn into the area specified by dst's coordinates, but I don't see anything, other than the background.
dst should be where you want to draw the image in the canvas, and the src should be the Rect you want crop from.
You might want to use a format that supports an alpha channel or load the bitmap as well as a greyscale image for the alpha channel, construct an image from both and draw that. Try Java's Graphics2D object. Here's an article that should get you started.

Categories