Strange canvas/bitmap issue - android

I have an Activity in which the user touches the eye positions on a picture, and this is supposed to draw a little white circle over each. I have a working bit of code that, using the Android FaceDetector tools, finds the eye positions and facial midpoint and draws a rectangle. The drawing part of that code, for reference, is this:
private void drawRectangles(){
Canvas canvas = new Canvas(mBitmap);
Paint paint = new Paint();
paint.setStrokeWidth(2);
paint.setColor(Color.BLUE);
paint.setStyle(Style.STROKE);
for (int i=0; i < faceFrames.length; i++) {
RectF r = faceFrames[i];
if (r != null){
canvas.drawRect(r, paint);
Log.d(TAG, "Drew rectangle");
}
}
mImageView.setImageResource(0);
mImageView.setImageBitmap(mBitmap);
mImageView.draw(canvas);
}
That part's fine. I figured, as a method that is called from onTouchEvent, that I could use the following to draw a circle:
private void makeDrawableLayer(int x, int y, int touchCount){
if (touchCount == 1){
Bitmap eyeOneBmp = Bitmap.createBitmap(mBitmap);
Canvas c1 = new Canvas(eyeOneBmp);
c1.drawCircle(x, y, 5, eyePaint);
mImageView.setImageResource(0);
mImageView.setImageBitmap(eyeOneBmp);
mImageView.draw(c1);
}
}
Here are screen shots showing the result of each code snippet. The first picture is the rectangle drawn on the face. The second picture shows the very strange result I get when I attempt to draw using the second code snippet. Note, however, that I had specified x and y as 10, 10 for the circle's position when drawing the second output. It's the same thing when I give it the passed-in eye position coordinates, just with the pixelated circle coming from wherever the eye is.
Does anyone have any idea what the heck is going on with this behavior?
Thanks so much.

So I found that you can basically only draw one time to the canvas before needing to make a class that extends View to start calling methods from. What I ended up needing to do was: customView.prepareCircle(), customView.invalidate(), and then parentView.addView(customView). And actually, I could only prepare, invalidate, and re-add the modified custom view to the canvas once before having to make any subsequent calls from a Runnable on the UI thread. I am not under the impression this is an ideal way to do it (certainly doesn't feel elegant), but it is giving me the results I want:

Related

Drawing a small Rect at the point where onTouch is detected

I'm performing a very small test to debug something larger, and am having trouble figuring out what's causing this issue.
I have an ImageView, a Canvas and a Bitmap. I initialize the ImageView and set it to the Bitmap, and then set the Canvas to the bitmap, as such:
_image.setImageBitmap(bitmap);
Canvas canvas = new Canvas(bitmap);
I then attach an onTouchListener to _image and listen for MotionEvent.ACTION_UP to be detected. At that point I draw a Rect on the Canvas, as such:
Rect rect = new Rect((int)event.getX(),event.getY(),event.getX()+20,event.getY()+20);
Paint paint = new Paint();
paint.setColor(Color.RED);
canvas.drawRect(rect,paint);
I've tried this with bitmap images of different sizes, to smaller than the full display, to very nearly the exact size, to larger, and I can never get the Rect to draw right where my finger touches the screen. I've tried different conversions on getX(), attempting to convert to dp, from dp, used getRawX/Y() instead; basically a bunch of different solutions, none of which have worked. I've tried getting the ratio of the bitmap's size to the screen size and multiplying/dividing by that; nothing has been able to solve this very simple issue.
Quite possibly I've tried the right approach at some point and simply implemented it incorrectly. I'd really appreciate some advice on what's causing the Rect to draw where it is, what conversion is needed to get it to draw right where the touch occurs, and why. The 'why' is because I want to learn from this.
Edit: This is an approximation of what happens. The red rectangle is where the actual touch occurs, and the blue is where it is drawn. It scales the further from X=Y=0 you get. Also, I just noticed that it also scales depending on the width and height of the image you're using.
So, I believe I've figured out the math which allows me to transform the onTouch coordinates onto the Canvas.
The reason I really wanted to go this route (as opposed to a custom subclass for ImageView) is because I'm not really that interested in the drawing of the rectangles: I'm more interested in accurately mapping onTouch coordinates onto specific OCRed text strings in the bitmap. The reason I was performing this exercise was to figure out why the touches weren't being attributed to the correct lines of text, and I was using drawn Rects to estimate where the touches were being recorded.
So, on to the math, which turned out to be a lot simpler than I'd feared:
int x1 = (int)(event.getX() * ((float) canvas.getWidth() / _image.getRight()));
int y1 = (int)(event.getY() * ((float) canvas.getHeight() / _image.getBottom()));
I multiply the onTouch coordinates by the ratio of the Canvas's dimensions to the ImageView's dimensions, which I cast to a float. Painting this as follows:
rect = new Rect(x1-10,y1-10,x1+10,y1+10);
//I took the advice about centering the Rect around the touch for clarity
paint = new Paint();
paint.setColor(Color.RED);
canvas.drawRect(rect,paint);
And the Rect is exactly where the touch was, regardless of bitmap size.
I found it interesting that it didn't cause any problems that I had called
_image.setAdjustViewBounds(true);
earlier in my program, which I did in order to remove some padding from the bitmap (which was showing up for some reason, possible due to the scaling). Also interesting is the fact that the ImageView is contained in a ScrollLayout, but even if the image is large enough to scroll though, it doesn't seem to need to take the scroll displacement into account.
Thanks to kcoppock for taking the time to help me out, and to the other user (whose comment was deleted when he realized he had misunderstood me) for taking the time as well.
I'd imagine your actual Bitmap is larger than the View and is scaled down. Since you're drawing in the coordinates of your bitmap, and that bitmap is being scaled down, the location will also be scaled down to match.
What you more likely want to do is make a subclass of ImageView, and then draw the Rect in onDraw() based on the location received in onTouchEvent(). Alternatively, you could do some math to figure out how much the bitmap is being scaled down relative to the view size, and scale up your coordinates by the inverse. A quick implementation of this touchable ImageView would be something like this:
public class DrawableImageView extends ImageView {
private final int mRectSize;
private final Paint mPaint;
private final Rect mRect = new Rect();
public DrawableImageView(Context context) {
super(context);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(Color.RED);
mRectSize = (int) (getResources().getDisplayMetrics().density * 20);
}
// Other constructors omitted for brevity
#Override public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
final int x = (int) event.getX();
final int y = (int) event.getY();
mRect.set(x, y, x + mRectSize, y + mRectSize);
mRect.offset(-(mRectSize / 2), -(mRectSize / 2));
invalidate();
return true;
} else {
return super.onTouchEvent(event);
}
}
#Override protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawRect(mRect, mPaint);
}
}

Animate bitmaps inside canvas

I'm doing a Live Wallpaper. I have a Runnable (which is updated every 5 seconds) where I call a draw() method which draws something on a Canvas. It calls a method in another class which is supposed to draw a series of bitmaps (with a delay, so it's animated). How would I change the code below, so that the next bitmap would be drawn with a delay?
int imageToDraw = 10;
while(imageToDraw>=0)
{
Bitmap b = BitmapFactory.decodeResource(mContext.getResources(), mContext.getResources().getIdentifier("image_"+imageToDraw, "raw", mContext.getPackageName()));
float centerX = (width - b.getWidth())/2;
float centerY = (height - b.getHeight())/2;
Paint p = new Paint();
p.setColor(Color.BLACK);
mCanvas.drawRect(0f, 0f, (float)width, (float)height, p); //draws a rectangle to clear the screen
mCanvas.drawBitmap(b, centerX, centerY, new Paint());
--imageToDraw;
b.recycle();
}
From Android's API Guide on Canvas and Drawables:
Draw your graphics directly to a Canvas. This way, you personally call
the appropriate class's onDraw() method (passing it your Canvas), or
one of the Canvas draw...() methods (like drawPicture()). In doing so,
you are also in control of any animation.
This means you have to perform the animation yourself, frame by frame. If you don't actually need to draw complex graphics, consider switching back to standard views so you can use help class like AnimationDrawable. Check here for an example of how to do your own animation in Canvas.

Best way to draw an image dynamically

I'm creating an android app that has to draw a map of an ambient that i have explored with laserscans.
I have e text file with all my data like:
index x y
path 0 0 0
path 1 1 0
path 2 2 0
...etc
obstacle 0 10 10
obstacle 1 10 22
..etc
so I have xy coordinates of where I've been and xy of obstacles I've seen.
I have a thread that reads the data from the text file and stores that data in a list.
Another thread reads that list and draws all the points that are put in the list until that moment by the reading thread.
My problem is that I don't want to re-read everything every time the reading thread has put new data into the data list. There is a way to draw something like a bitmap and modify this dynamically? I mean that every time I have read some new data I "open" the bitmap, I add to that the new points, "close" that bitmap and show on the screen?
what I am doing now is to read all the list in my onDraw() function and draw point by point, but I have 170 000 points and that is a useful work because every time the points are in the old position, I only have some new points...
You can create a bitmap and a canvas in your view and just continue to draw into this bitmap as necessary. To prevent points from being drawn over again, the thread that draws the points should either remove points from the list as they are drawn, or keep track of the index of the last point.
Here's an example that contains the basics:
public class myView extends View {
Bitmap mBitmap = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888);
Canvas mCanvas = new Canvas(mBitmap);
Paint mPaint = new Paint();
public void updateBitmap(List<Point> points) {
while (!points.isEmpty()) {
int x = points.get(0).x;
int y = points.get(0).y;
mCanvas.drawPoint(x, y, mPaint);
points.remove(0);
}
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(mBitmap, 0, 0, null);
}
}
The thread that draws the points calls updateBitmap, passing it the current list of points to draw. These points are then removed from the list so they will not be drawn again later on.

How to draw circle when Tab event is fired

I am learning android Live wallpaper development. I found an awesome template in the AndEngine Forums
In this template I found an overridable method OnTab which provides 2 parameters i.e x coordintate & y coordinate .
protected void onTap(final int pX, final int pY)
{
SurfaceHolder holder= //Get current surface holder object
Paint paint = new Paint();
Canvas canvas= holder.lockCanvas();
paint.setColor(Color.WHITE);
canvas.drawCircle(20, 50, 25, paint);
}
I want to draw a circle when user tabs or touches the screen but i am finding it difficult to get the sufaceholder object which will let me draw a circle on the canvas Or can i achieve this some other way?
You need to do the drawing within the onDraw() method. When a touch is occurring you should save the X and Y location and then in the onDraw() method draw the circle.

Sprite Rotation in Android using Canvas.DrawBitmap. I am close, what am I doing wrong?

I have this sprite rotating algorithm (its poorly named and just used for testing). It is so close, sprites drawn with it do rotate. Everyframe I can add +5 degrees to it and see my nice little sprite rotate around. The problem is, the other stuff drawn to the canvas now flickers. If I don't do the rotation the regular drawn sprites work great. I think I am close but I just don't know what piece I am missing. Below is my two "Draw_Sprite" methods, one just draws the previously resource loaded bitmap to the canvas passed in. The other one, does some rotation the best I know how to rotate the sprite by so x many degrees..and then draw it. If I have a nice game loop that draws several objects, one type is the rotated kind. Then the non-rotated sprites flicker and yet the rotated sprite never does. Though if I draw the non-rotated sprites first, all is well, but then the Z-Ordering could be messed up (sprites on top of UI elements etc)... The method definitions:
/*************************************************
* rotated sprite, ignore the whatever, its for ease of use and testing to have this argument list
* #param c canvas to draw on.
* #param whatever ignore
* #param rot degrees to rotate
* #return
*/
public int Draw_Sprite(Canvas c, int whatever, int rot) {
//rotating sprite
Rect src = new Rect(0, 0, width, height);
Rect dst = new Rect(x, y, x + width, y + height);
Matrix orig = c.getMatrix();
mMatrix = orig;
orig.setTranslate(0, 0);
orig.postRotate(rot, x+width/2, y+height/2);
c.setMatrix(orig);
c.drawBitmap(images[curr_frame], src, dst, null);
c.setMatrix(mMatrix); //set it back so all things afterwards are displayed correctly.
isScaled=false;
return 1;
}
/********************************************************
* draw a regular sprite to canvas c
* #param c
* #return
*/
public int Draw_Sprite(Canvas c) {
Rect src = new Rect(0, 0, width, height);
Rect dst = new Rect(x, y, x + width, y + height);
c.drawBitmap(images[curr_frame], src, dst, null);
isScaled=false;
return 1;
}
And now the usage:
void onDraw(Canvas c)
{
canvas.drawRect( bgRect, bgPaint); //draw the background
//draw all game objects
// draw the normal items
for (GameEntity graphic : _graphics) {
graphic.toScreenCoords((int)player_x, (int)player_y);
if(graphic.getType().equals("planet")) //draw planets
graphic.Draw_Sprite(canvas); //before the rotation call draws fine
else
{
//rotate all space ships every frame so i see them spinning
//test rotation
mRot +=5;
if(mRot>=360)
mRot=0;
graphic.Draw_Sprite(canvas, 0, mRot); //yes function name will be better in future. this rotates spins draws fine
}
}
thePlayer.Draw_Sprite(canvas); //FLICKERS
drawUI(canvas);//all things here flickr
}
So it does do it, things after a call to a rotational draw are drawn correctly. But the problem is it flickrs. Now One could say I should just do all my non rotational stuff and save that last, but the zordering would be off.... suggestions as to how to tackle this issue of zordering or the flickering?
Just for the next guy who may read this. You can do this with only a few lines of code:
canvas.save();
canvas.rotate(rotation_angle, x + (widthofimage / 2), y + (heightofimage / 2));
canvas.drawBitmap(bitmap, x, y, null);
canvas.restore();
Try using canvas.save() before the rotation and canvas.restore() after manipulation is complete.
When performing manipulations on the canvas in order to change the way an object is drawn you have to remember the manipulations set how the canvas handles origins etc... So if you translate or rotate the canvas, that will be set for the lifetime of that canvas. In order to avoid this you first call save, which saves a snapshot of the canvas matrix before you manipulate it, then you run all your changes, then call restore which will restore the canvas back to the last saved point. Otherwise all your changes build up and you get unintended results.

Categories

Resources