title: Canvas Pinch-Zoom to Point Within Bounds
body: <p>I've been stuck on this problem for eight hours, so I figured it was time to get some help.</p>
  
  <p>Before I begin my problem, I'll just let it be known that I've been through this site and Google, and none of the answers I've found have helped. (<a href="https://stackoverflow.com/questions/8848523/canvas-zoom-goes-to-point-0-0">This</a> is one, <a href="https://stackoverflow.com/questions/6835224/android-bitmap-canvas-offset-after-scale">another</a>, and <a href="https://stackoverflow.com/questions/6624103/scaling-image-of-imageview-while-maintaining-center-point-in-same-place">another</a>.)</p>
  
  <p>Here's the deal: I have a class that extends <code>SurfaceView</code> (let's call it <code>MySurface</code>) and overrides many methods in it. Normally, it draws several squares and text boxes, which is all fine. As soon as a user starts touching, it converts to a <code>Bitmap</code>, then draws each frame that until the user releases.</p>
  
  <p>Here's the rub: I want to implement such a functionality that the user can place two fingers on the screen, pinch to zoom, and also pan around (but ONLY with two fingers down).</p>
  
  <p>I found a few implementations of pinch-to-zoom and adapted them to my <code>Canvas</code> object in <code>MySurface</code> via the following:</p>
  
  <pre><code>public void onDraw(Canvas canvas) {
      super.onDraw(canvas);
  
      canvas.save();
  
      canvas.scale(mScaleVector.z, mScaleVector.z); // This is the scale factor as seen below
      canvas.translate(mScaleVector.x, mScaleVector.y); // These are offset values from 0,0, both working fine
  
      // Start draw code
  
      // ...
  
      // End draw code
  
      canvas.restore();
  }
  private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
      @Override
      public boolean onScale(ScaleGestureDetector detector) {
          float factor = detector.getScaleFactor();
          if (Math.abs(factor - 1.0f) &gt;= 0.0075f) {
              mScaleVector.z *= factor;
              mScaleVector.z = Math.max(MIN_ZOOM, Math.min(mScaleVector.z, MAX_ZOOM));
          }
  
          // ...
  
          invalidate();
  
          return true;
      }
  }
  public boolean onTouchEvent(MotionEvent event) {
      int action = event.getAction() &amp; MotionEvent.ACTION_MASK;
      int pointerIndex = (event.getAction() &amp; MotionEvent.ACTION_POINTER_INDEX_MASK) &gt;&gt; MotionEvent.ACTION_POINTER_INDEX_SHIFT;
      if (event.getPointerCount() == 2) {
          if (action == MotionEvent.ACTION_POINTER_DOWN &amp;&amp; pointerIndex == 1) {
              // The various pivot coordinate codes would belong here
          }
      }
  
      detector.onTouchEvent(event); // Calls the Scale Gesture Detector
      return true;
  }
  </code></pre>
  
  <p>While both elements work fine--the scrolling back and forth and the pinch-to-zoom--there is one large problem. The pinch-to-zoom, when used, zooms into the point <code>0,0</code>, instead of zooming into the finger point.</p>
  
  <p>I've tried a lot of ways to fix this:</p>
  
  <ul>
  <li>Using <code>canvas.scale(mScaleVector.z, mScaleVector.z, mScaleVector.x, mScaleVector.y);</code>; obviously, this produces unwanted results as the <code>mScaleVector</code> x and y values are 0-offsets.</li>
  <li>Managing a "pivot" coordinate that uses the same offset as the <code>translate()</code> method, but this produces either the same <code>0,0</code> issue, or jumping around when the view is touched.</li>
  <li>Numerous other things... I've done a lot with the aforementioned pivot coordinate, trying to base its location on the user's first touch, and moving it relative to that touch each successive gesture.</li>
  </ul>
  
  <p>Additionally, this canvas must be bounded, so the user cannot scroll forever. However, when I use the <code>.scale(sx, sy, px, py)</code> method, it pushes things beyond any bounds I set in <code>.translate()</code>.</p>
  
  <p>I'm... pretty much open to anything at this point. I know this functionality can be added, as it is seen in the Android 4.0 gallery (when viewing a single image). I've tried to track down the source code that handles this, to no avail.</p>