protected internal override void ComputeRender(float renderProgress)
        {
            RectF arcBounds = mCurrentBounds;

            //compute gas tube bounds
            mGasTubeBounds.Set(arcBounds.CenterX() - mGasTubeWidth / 2.0f, arcBounds.CenterY(), arcBounds.CenterX() + mGasTubeWidth / 2.0f, arcBounds.CenterY() + mGasTubeHeight);
            //compute pipe body bounds
            mPipeBodyBounds.Set(arcBounds.CenterX() + mGasTubeWidth / 2.0f - mPipeBodyWidth / 2.0f, arcBounds.CenterY() - mPipeBodyHeight, arcBounds.CenterX() + mGasTubeWidth / 2.0f + mPipeBodyWidth / 2.0f, arcBounds.CenterY());
            //compute cannula bounds
            mCannulaBounds.Set(arcBounds.CenterX() + mGasTubeWidth / 2.0f - mCannulaWidth / 2.0f, arcBounds.CenterY() - mCannulaHeight - mCannulaOffsetY, arcBounds.CenterX() + mGasTubeWidth / 2.0f + mCannulaWidth / 2.0f, arcBounds.CenterY() - mCannulaOffsetY);
            //compute balloon bounds
            float insetX = mBalloonWidth * 0.333f * (1 - mProgress);
            float insetY = mBalloonHeight * 0.667f * (1 - mProgress);

            mBalloonBounds.Set(arcBounds.CenterX() - mGasTubeWidth / 2.0f - mBalloonWidth / 2.0f + insetX, arcBounds.CenterY() - mBalloonHeight + insetY, arcBounds.CenterX() - mGasTubeWidth / 2.0f + mBalloonWidth / 2.0f - insetX, arcBounds.CenterY());

            if (renderProgress <= START_INHALE_DURATION_OFFSET)
            {
                mCannulaBounds.Offset(0, -mCannulaMaxOffsetY * renderProgress / START_INHALE_DURATION_OFFSET);

                mProgress     = 0.0f;
                mProgressText = 10 + PERCENT_SIGN;

                mPaint.TextSize = mTextSize;
                mPaint.GetTextBounds(mProgressText, 0, mProgressText.Length, mProgressBounds);
            }
            else
            {
                float exhaleProgress = ACCELERATE_INTERPOLATOR.GetInterpolation(1.0f - (renderProgress - START_INHALE_DURATION_OFFSET) / (1.0f - START_INHALE_DURATION_OFFSET));
                mCannulaBounds.Offset(0, -mCannulaMaxOffsetY * exhaleProgress);

                mProgress     = 1.0f - exhaleProgress;
                mProgressText = AdjustProgress((int)(exhaleProgress * 100.0f)) + PERCENT_SIGN;

                mPaint.TextSize = mTextSize;
                mPaint.GetTextBounds(mProgressText, 0, mProgressText.Length, mProgressBounds);
            }
        }
		//Configures the neccesary matrix transformation to apply to the textureView
		public void configureTransform(int viewWidth, int viewHeight) 
		{
			if (null == Activity || null == previewSize || null == textureView) 
				return;

			int rotation = (int)Activity.WindowManager.DefaultDisplay.Rotation;
			var matrix = new Matrix ();
			var viewRect = new RectF (0, 0, viewWidth, viewHeight);
			var bufferRect = new RectF (0, 0, previewSize.Height, previewSize.Width);
			float centerX = viewRect.CenterX();
			float centerY = viewRect.CenterY();
			if ((int)SurfaceOrientation.Rotation90 == rotation || (int)SurfaceOrientation.Rotation270 == rotation) { 
				bufferRect.Offset ((centerX - bufferRect.CenterX()), (centerY - bufferRect.CenterY()));
				matrix.SetRectToRect (viewRect, bufferRect, Matrix.ScaleToFit.Fill);
				float scale = System.Math.Max (
					(float)viewHeight / previewSize.Height,
					(float)viewHeight / previewSize.Width);
				matrix.PostScale (scale, scale, centerX, centerY);
				matrix.PostRotate (90 * (rotation - 2), centerX, centerY);
			}
			textureView.SetTransform (matrix);
		}
        // Grows the cropping rectange by (dx, dy) in image space.
        private void growBy(float dx, float dy)
        {
            if (maintainAspectRatio)
            {
                if (dx != 0)
                {
                    dy = dx / initialAspectRatio;
                }
                else if (dy != 0)
                {
                    dx = dy * initialAspectRatio;
                }
            }

            // Don't let the cropping rectangle grow too fast.
            // Grow at most half of the difference between the image rectangle and
            // the cropping rectangle.
            RectF r = new RectF(cropRect);
            if (dx > 0F && r.Width() + 2 * dx > imageRect.Width())
            {
                float adjustment = (imageRect.Width() - r.Width()) / 2F;
                dx = adjustment;
                if (maintainAspectRatio)
                {
                    dy = dx / initialAspectRatio;
                }
            }
            if (dy > 0F && r.Height() + 2 * dy > imageRect.Height())
            {
                float adjustment = (imageRect.Height() - r.Height()) / 2F;
                dy = adjustment;
                if (maintainAspectRatio)
                {
                    dx = dy * initialAspectRatio;
                }
            }

            r.Inset(-dx, -dy);

            // Don't let the cropping rectangle shrink too fast.
            float widthCap = 25F;
            if (r.Width() < widthCap)
            {
                r.Inset(-(widthCap - r.Width()) / 2F, 0F);
            }
            float heightCap = maintainAspectRatio
                ? (widthCap / initialAspectRatio)
                    : widthCap;
            if (r.Height() < heightCap)
            {
                r.Inset(0F, -(heightCap - r.Height()) / 2F);
            }

            // Put the cropping rectangle inside the image rectangle.
            if (r.Left < imageRect.Left)
            {
                r.Offset(imageRect.Left - r.Left, 0F);
            }
            else if (r.Right > imageRect.Right)
            {
                r.Offset(-(r.Right - imageRect.Right), 0);
            }
            if (r.Top < imageRect.Top)
            {
                r.Offset(0F, imageRect.Top - r.Top);
            }
            else if (r.Bottom > imageRect.Bottom)
            {
                r.Offset(0F, -(r.Bottom - imageRect.Bottom));
            }

            cropRect.Set(r);
            DrawRect = computeLayout();
            context.Invalidate();
        }
		/// <summary>
		/// Configures the necessary transformation to mTextureView.
		/// This method should be called after the camera preciew size is determined in openCamera, and also the size of mTextureView is fixed
		/// </summary>
		/// <param name="viewWidth">The width of mTextureView</param>
		/// <param name="viewHeight">VThe height of mTextureView</param>
		private void ConfigureTransform(int viewWidth, int viewHeight)
		{
			Activity activity = Activity;
			if (mTextureView == null || mPreviewSize == null || activity == null) {
				return;
			}

			SurfaceOrientation rotation = activity.WindowManager.DefaultDisplay.Rotation;
			Matrix matrix = new Matrix ();
			RectF viewRect = new RectF (0, 0, viewWidth, viewHeight);
			RectF bufferRect = new RectF (0, 0, mPreviewSize.Width, mPreviewSize.Height);
			float centerX = viewRect.CenterX();
			float centerY = viewRect.CenterY();
			if (rotation == SurfaceOrientation.Rotation90 || rotation == SurfaceOrientation.Rotation270) {
				bufferRect.Offset (centerX - bufferRect.CenterX(), centerY - bufferRect.CenterY());
				matrix.SetRectToRect (viewRect, bufferRect, Matrix.ScaleToFit.Fill);
				float scale = Math.Max ((float)viewHeight / mPreviewSize.Height, (float)viewWidth / mPreviewSize.Width);
				matrix.PostScale (scale, scale, centerX, centerY);
				matrix.PostRotate (90 * ((int)rotation - 2), centerX, centerY);
			}
			mTextureView.SetTransform (matrix);
		}