public void rotateImage(int degrees) {
    if (mBitmap != null) {
      // Force degrees to be a non-zero value between 0 and 360 (inclusive)
      if (degrees < 0) {
        degrees = (degrees % 360) + 360;
      } else {
        degrees = degrees % 360;
      }

      boolean flipAxes =
          !mCropOverlayView.isFixAspectRatio()
              && ((degrees > 45 && degrees < 135) || (degrees > 215 && degrees < 305));
      BitmapUtils.RECT.set(mCropOverlayView.getCropWindowRect());
      float halfWidth = (flipAxes ? BitmapUtils.RECT.height() : BitmapUtils.RECT.width()) / 2f;
      float halfHeight = (flipAxes ? BitmapUtils.RECT.width() : BitmapUtils.RECT.height()) / 2f;
      if (flipAxes) {
        boolean isFlippedHorizontally = mFlipHorizontally;
        mFlipHorizontally = mFlipVertically;
        mFlipVertically = isFlippedHorizontally;
      }

      mImageMatrix.invert(mImageInverseMatrix);

      BitmapUtils.POINTS[0] = BitmapUtils.RECT.centerX();
      BitmapUtils.POINTS[1] = BitmapUtils.RECT.centerY();
      BitmapUtils.POINTS[2] = 0;
      BitmapUtils.POINTS[3] = 0;
      BitmapUtils.POINTS[4] = 1;
      BitmapUtils.POINTS[5] = 0;
      mImageInverseMatrix.mapPoints(BitmapUtils.POINTS);

      // This is valid because degrees is not negative.
      mDegreesRotated = (mDegreesRotated + degrees) % 360;

      applyImageMatrix(getWidth(), getHeight(), true, false);

      // adjust the zoom so the crop window size remains the same even after image scale change
      mImageMatrix.mapPoints(BitmapUtils.POINTS2, BitmapUtils.POINTS);
      mZoom /=
          Math.sqrt(
              Math.pow(BitmapUtils.POINTS2[4] - BitmapUtils.POINTS2[2], 2)
                  + Math.pow(BitmapUtils.POINTS2[5] - BitmapUtils.POINTS2[3], 2));
      mZoom = Math.max(mZoom, 1);

      applyImageMatrix(getWidth(), getHeight(), true, false);

      mImageMatrix.mapPoints(BitmapUtils.POINTS2, BitmapUtils.POINTS);

      // adjust the width/height by the changes in scaling to the image
      double change =
          Math.sqrt(
              Math.pow(BitmapUtils.POINTS2[4] - BitmapUtils.POINTS2[2], 2)
                  + Math.pow(BitmapUtils.POINTS2[5] - BitmapUtils.POINTS2[3], 2));
      halfWidth *= change;
      halfHeight *= change;

      // calculate the new crop window rectangle to center in the same location and have proper
      // width/height
      BitmapUtils.RECT.set(
          BitmapUtils.POINTS2[0] - halfWidth,
          BitmapUtils.POINTS2[1] - halfHeight,
          BitmapUtils.POINTS2[0] + halfWidth,
          BitmapUtils.POINTS2[1] + halfHeight);

      mCropOverlayView.resetCropOverlayView();
      mCropOverlayView.setCropWindowRect(BitmapUtils.RECT);
      applyImageMatrix(getWidth(), getHeight(), true, false);
      handleCropWindowChanged(false, false);

      // make sure the crop window rectangle is within the cropping image bounds after all the
      // changes
      mCropOverlayView.fixCurrentCropWindowRect();
    }
  }
Rotates image by the specified number of degrees clockwise.<br>
Negative values represent counter-clockwise rotations.

@param degrees Integer specifying the number of degrees to rotate.
core/src/main/java/com/theartofdev/edmodo/cropper/CropImageView.java