/*
* Copyright 2011 Jon A. Webb
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package root.gast.image;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.graphics.RectF;
import android.hardware.Camera;
import android.util.AttributeSet;
import android.widget.ImageView;
/**
* Displays an image while tracking the camera display orientation.
*
* @author Jon A. Webb <<a
* href="mailto:jonawebb@gmail.com">jonawebb@gmail.com</a>>
*
*/
public class ImageCameraView extends ImageView
{
private boolean mbMirror = false;
private int mnDisplayOrientation = 0;
private ScaleType mScaleType;
public ImageCameraView(Context context)
{
super(context);
}
public ImageCameraView(Context context, AttributeSet attrs)
{
super(context, attrs);
}
public ImageCameraView(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
}
public void setCameraDisplayCharacteristics(int nCameraFacing,
int nDisplayOrientation)
{
mbMirror = nCameraFacing == Camera.CameraInfo.CAMERA_FACING_FRONT;
mnDisplayOrientation = nDisplayOrientation;
}
/**
* In the override we apply the camera transformation and map the image to
* the display.
*
* @see android.widget.ImageView#setImageBitmap(android.graphics.Bitmap)
*/
@Override
public void setImageBitmap(Bitmap bitmap)
{
Matrix matrix = new Matrix();
matrix.reset();
// apply the camera display characteristics. First mirror the image...
if (mbMirror)
{
matrix.postScale(-1f, 1f);
}
// then rotate it...
matrix.postRotate(mnDisplayOrientation);
/**
* Math is hard!
* -- Barbie
*
* We could calculate the matrix that will map the rotated and possibly
* mirrored image to the display, but it is much easier to compose a
* matrix that gets the width and height right, and then translate it so
* the top left corner maps to (0,0). This will correctly fit the image
* to the display.
*
* Rotated image width and height must match display width and height
* see where the image rectangle maps now.
*/
RectF rect = new RectF(0, 0, bitmap.getWidth(), bitmap.getHeight());
matrix.mapRect(rect);
switch (mScaleType)
{
case FIT_XY:
// get the width and height of the image right
matrix.postScale(this.getWidth() / rect.width(),
this.getHeight() / rect.height());
break;
case CENTER_CROP:
/*
* "Scale the image uniformly (maintain the image's aspect ratio) so
* that both dimensions (width and height) of the image will be
* equal to or LARGER than the corresponding dimension of the view
* (minus padding)." (Android documentation)
*/
{
float scale = Math.max(this.getWidth() / rect.width(),
this.getHeight() / rect.height());
matrix.postScale(scale, scale);
break;
}
case CENTER:
// everything is done below in if statement
// translate center of image to center of display
break;
case CENTER_INSIDE:
case FIT_START:
case FIT_END:
case FIT_CENTER:
{
/**
* "Scale the image uniformly (maintain the image's aspect
* ratio) so that both dimensions (width and height) of the
* image will be equal to or LESS than the corresponding
* dimension of the view (minus padding)." (Android documentation)
*/
float scale = Math.min(this.getWidth() / rect.width(),
this.getHeight() / rect.height());
matrix.postScale(scale, scale);
break;
}
}
// we translate the image to obtain the other part of the postcondition.
// Get the mapping of the image rectangle before translation.
rect = new RectF(0, 0, bitmap.getWidth(), bitmap.getHeight());
matrix.mapRect(rect);
if (mScaleType == ScaleType.CENTER
|| mScaleType == ScaleType.FIT_CENTER)
{
// translate center of image to center of display
matrix.postTranslate(this.getWidth() / 2 - rect.centerX(),
this.getHeight() / 2 - rect.centerY());
} else if (mScaleType == ScaleType.FIT_START
|| mScaleType == ScaleType.FIT_XY)
{
// now translate the image so the top left corner maps to (0,0)
matrix.postTranslate(-rect.left, -rect.top);
} else if (mScaleType == ScaleType.FIT_END)
{
// translate the image so the bottom right corner maps to (width(),
// height())
matrix.postTranslate(getWidth() - rect.width(),
getHeight() - rect.height());
}
super.setScaleType(ScaleType.MATRIX);
super.setImageMatrix(matrix);
super.setImageBitmap(bitmap);
}
/**
* We override setScaleType so the actual scaletype used is always matrix
* but we model the behavior of the other scaletypes. This allows this class's
* matrix functions to be used to determine where things should go on the displayed
* image.
* @see android.widget.ImageView#setScaleType(android.widget.ImageView.ScaleType)
*/
@Override
public void setScaleType(ScaleType scaleType)
{
if (scaleType == ScaleType.MATRIX)
{
// I can't figure out how to support this
throw new UnsupportedOperationException(
"ScaleType.MATRIX is not supported by ImageCameraView");
}
mScaleType = scaleType;
}
}