title: (Crop Image) Map crop rectangle from zoomed image to original image
body: <p>I am trying to implement Image cropping in Android following the source code from AOSP gallery app (code actually taken from this <a href="https://github.com/jdamcd/android-crop" rel="nofollow">library</a>). I have set the <code>ImageView</code>'s scale type to be <code>MATRIX</code> and scaled and translated the image using <code>posttranslate</code> and <code>postscale</code> method on the image matrix.</p>
  
  <p>A crop rectangle of fixed 260x260 pixel is drawn on the image, centered within the <code>ImageView</code>. This crop rectangle is fixed in size and can't be moved. Instead the image is allowed to be moved and scaled (zoom in/ zoom out) so that user can put the desired area of the image within the crop rectangle. </p>
  
  <p>while the user moves the image by <code>dx</code>, <code>dy</code> amount, <code>posttranslate(dx, dy)</code> is called on the image's matrix. Then the crop rectangle is mapped to the current matrix by calling <code>matrix.map(rect)</code>. And then the new position(on original image) of the crop rectangle is calculated by converting the <code>dx</code>, <code>dy</code> to image space and shifting the crop rectangle accordingly. </p>
  
  <p>here's some source excerpt for moving the image:</p>
  
  <pre><code>// This matrix is recomputed when we go from the thumbnail image to
  // the full size image.
  protected Matrix baseMatrix = new Matrix();
  
  // This is the supplementary transformation which reflects what
  // the user has done in terms of zooming and panning.
  //
  // This matrix remains the same when we go from the thumbnail image
  // to the full size image.
  protected Matrix suppMatrix = new Matrix();
  
  // This is the final matrix which is computed as the concatentation
  // of the base matrix and the supplementary matrix.
  private final Matrix displayMatrix = new Matrix();
  
  //move image by dx dy
  protected void panBy(float dx, float dy) {
      postTranslate(dx, dy);
      setImageMatrix(getImageViewMatrix());
  }
  
   protected void postTranslate(float dx, float dy) {
      suppMatrix.postTranslate(dx, dy);
  } 
  
  // Combine the base matrix and the supp matrix to make the final matrix
  protected Matrix getImageViewMatrix() {
      // The final matrix is computed as the concatentation of the base matrix
      // and the supplementary matrix
      displayMatrix.set(baseMatrix);
      displayMatrix.postConcat(suppMatrix);
      return displayMatrix;
  }
  </code></pre>
  
  <p>And this is how the image movement affects the crop rectangle view</p>
  
  <pre><code>public void handlePan(float dx, float dy)
  {
     RectF r = computeLayout();
  
     float xDelta = (dx * (cropRect.width() / r.width()));
     float yDelta = -(dy * (cropRect.height() / r.height()));
     cropRect.offset(xDelta, yDelta);
  
     // Put the cropping rectangle inside image rectangle
     cropRect.offset(
             Math.max(0, imageRect.left - cropRect.left),
             Math.max(0, imageRect.top  - cropRect.top));
  
     cropRect.offset(
             Math.min(0, imageRect.right  - cropRect.right),
             Math.min(0, imageRect.bottom - cropRect.bottom));
  }
  
  private RectF computeLayout() {
      RectF r = new RectF(cropRect.left, cropRect.top,
                          cropRect.right, cropRect.bottom);
      matrix.mapRect(r);
      return r;
  }
  </code></pre>
  
  <p>The crop view gets the image's current <code>matrix</code> from the following <code>getunrotatedmatrix</code> function</p>
  
  <pre><code> public Matrix getUnrotatedMatrix(){
      Matrix unrotated = new Matrix();
      getProperBaseMatrix(bitmapDisplayed, unrotated, false);
      unrotated.postConcat(suppMatrix);
      return unrotated;
  }
  
   // Setup the base matrix so that the image is centered and scaled properly.
  private void getProperBaseMatrix(RotateBitmap bitmap, Matrix matrix, boolean includeRotation) {
      float viewWidth = getWidth();
      float viewHeight = getHeight();
  
      float w = bitmap.getWidth();
      float h = bitmap.getHeight();
      matrix.reset();
  
      // We limit up-scaling to 3x otherwise the result may look bad if it's a small icon
      float widthScale = Math.min(viewWidth / w, 3.0f);
      float heightScale = Math.min(viewHeight / h, 3.0f);
      float scale = Math.min(widthScale, heightScale);
  
      if (includeRotation) {
          matrix.postConcat(bitmap.getRotateMatrix());
      }
      matrix.postScale(scale, scale);
      matrix.postTranslate((viewWidth  - w * scale) / 2F, (viewHeight - h * scale) / 2F);
  }
  </code></pre>
  
  <p>This works perfectly. But what I am struggling with is, to handle the zooming of the image. I have tried mapping a rectangle to the matrix and then mapping this rectangle to the original image, scaling down the crop rect by the same amount the image is scaled up and vice versa. But none of them worked. Any idea how to achieve this(get the position of the crop rectangle on original image(on sdcard), after zooming the image in imageview)? The zooming is done as below: </p>
  
  <pre><code>protected void zoomTo(float scaleFactor, float centerX, float centerY) {
      float oldScale = getScale();
      float scale = oldScale*scaleFactor;
  
      if (scale &gt; maxZoom) {
          scale = maxZoom;
          scaleFactor=maxZoom/oldScale;
      }
      else if(scale&lt;0.5)
      {
          scale=0.5f;
          scaleFactor=0.5f/oldScale;
      }
  
      suppMatrix.postScale(scaleFactor, scaleFactor, centerX, centerY);
      setImageMatrix(getImageViewMatrix());
  }
  </code></pre>
  
  <p>I understand that the crop rectangle has to be made smaller(to point to a smaller region of original image) after zoom-in and vice versa. But how to exactly do that scaling and position the crop rectangle appropriately considering the center of zoom (which can be anywhere on the image even outside the crop rectangle).  </p>