Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android Canvas, multiple paths with different zoom amounts

It's supposed to be a simple whiteboard kind of tool, with zoom/panning effects.

Everything is alright with the entire drawing of the paths part, except for when it comes down to zooming/panning.

When a zoom is applied to the whiteboard, no matter the way I do, all the lines that come after the zoom applied get displaced in respect with the position I actually did the stroking.

I'll try to expose the details as good as it's possible, without showing unnecessary code, still it's quite a long post, please, bear with me.

As an image is worth a thousand words, I will illustrate the situation:

enter image description here

enter image description here

enter image description here

enter image description here

So, I've tried many different approaches, without any success.

Now down to the technical part, this is what I'm doing right now:

It's implemented extending the View.

I use the onDraw method only to perform the 'animation' of the strokes being applied:

@Override
public void onDraw(Canvas canvas) 
{
    super.onDraw(canvas);

    if(isDrawing)
    {
        canvas.drawPath(mPath,  mPaint);
        canvas.drawBitmap(mBitmap, 0, 0, mPaint);   
    }
}

I have two ImageViews that I use to place the resulting bitmap right after the drawing and zooming are performed. The reason I use two views is to swap them in order to always have one available view at the drawing rendering moment.

 <ImageView
 android:id="@+id/imageView"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:layout_alignParentLeft="true"  
 android:layout_alignParentTop="true"    
 android:visibility="visible"     
 android:scaleType="matrix" />

 <ImageView     
 android:id="@+id/imageView2"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:layout_alignParentLeft="true"          
 android:layout_alignParentTop="true"    
 android:visibility="visible"     
 android:scaleType="matrix" />

After something is drawn to the canvas, I send the given bitmap to my view, then clean the canvas to set it brand new to more drawings:

 private void sendImage2BackView()
 {
     //veeery extensive and boring code surpressed here

     //set the entire hocus-pocus' resulting bitmap as a resource for my view
     mView.setImageBitmap(tempBmp);
     mView.bringToFront();  


 }

I implement a ScaleListener class that extends ScaleGestureDetector.SimpleOnScaleGestureListener

During the onScale moments, I tell my canvas what my scalling proportion is:

@Override
public boolean onScale(ScaleGestureDetector detector) 
{   
    drawAction = ZOOM;      
    scaleFactor *= detector.getScaleFactor();
    scaleFactor = Math.max(MIN_ZOOM, Math.min(scaleFactor, MAX_ZOOM));

    canvas.setScaleFactor(scaleFactor);
    return true;
}

Then, at my View ( canvas obj ), I just set the scale to the view being used:

    mView.setScaleX(factor);
    mView.setScaleY(factor);     

Now, for the most important part, at the onScaleEnd, I call a method which I attempt to get the current canvas bitmap AS IS and send it to my view to abide peacefully with its siblings, each one its own size and offset.

    public void setScaleEnd()
    {
         //Lots of more boring code surpressed here
         //....

         //I store the bitmap that was contained at the ImageView
         // IF anyone is present
         previousBmp = ((BitmapDrawable)mView.getDrawable()).getBitmap();

         //then, I create a brand new scaled bitmap,in order to 
         // acquire the same proportion of ZOOM that I've just applied
         tempBmp = Bitmap.createScaledBitmap(previousBmp,
            (int)(mView.getWidth()*scaleFactor),
            (int)(mView.getHeight()*scaleFactor), true);


         int height = (int)(mView.getHeight()*scaleFactor);
         int width = (int)(mView.getWidth()*scaleFactor);

         //set my matrix
         mMatrix = new Matrix();

         //give it the right proportion
         mMatrix.postScale(scaleFactor, scaleFactor);

         translationX = mView.getWidth() / 2 - width / 2;
         translationY = mView.getHeight() / 2 - height / 2;

         //give it some necessary translation amount
         mMatrix.postTranslate(translationX, translationY);

         //set the matrix to the view, and send the resized bitmap to my ImageView.
         mView.setImageMatrix(mMatrix);
         mView.setImageBitmap(tempBmp);
    }

Now, I have tried during everything I just showed straight to the canvas, giving it a scale/translate amount; tried working separately with bitmaps then sent them back to a new clean canvas.

Still I keep getting that displacement on my strokes after a given amount of ZOOM is applied!!! I just want to be able to draw; give it a zoom; then draw some more things, and have the strokes stay where I'm drawing!

Is that possible?

I was just playing with S Note app, it does exactly what I'm trying to achieve.

like image 367
Former User of Mine Avatar asked Oct 15 '14 20:10

Former User of Mine


1 Answers

I want to propose some solution to you, but I'm not sure it will works.

At first I need to clarify, that you've done a big mistake in this code:

     tempBmp = Bitmap.createScaledBitmap(previousBmp,
        (int)(mView.getWidth()*scaleFactor),
        (int)(mView.getHeight()*scaleFactor), true);

Let's assume that you have a tablet with 1280x800 pixels and scale factor 4x or even 10x. You will try to allocate 16Mb and 40Mb Bitmap accordingly!

It's so easy to get OutOfMemoryError thrown here, so you need to use conceptually another approach.

I advice you to use Picture class.

  1. You need to determine which size of original picture do you provide to the user?
  2. After that you need to create the Canvas object around your picture using method Picture.beginRecord(int width, int height)
  3. Now you can apply scale factor, translate the canvas and perform drawing operations.
  4. Inside the onDraw() method you need to draw your picture using method Picture.draw(Canvas canvas) with corresponding settings (scale factor, translate) applied to canvas.

I didn't try this solution, but I hope that the Picture class will help you.

like image 81
Oleksii K. Avatar answered Sep 28 '22 20:09

Oleksii K.