Skip to content

Commit

Permalink
Adjust TextureView size to the camera aspect ratio
Browse files Browse the repository at this point in the history
This also introduces caching to AspectRatio.

Change-Id: If2e5b32412e7593ff320d06421d55b2340c823d2
  • Loading branch information
yaraki committed Apr 4, 2016
1 parent a4f9936 commit 129e2d9
Show file tree
Hide file tree
Showing 10 changed files with 186 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,9 @@

import com.google.android.cameraview.CameraView;

import android.os.Build;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;

public class MainActivity extends AppCompatActivity {

Expand All @@ -34,10 +32,6 @@ public class MainActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (Build.VERSION.SDK_INT >= 16) {
// Hide the status bar
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN);
}
mCameraView = (CameraView) findViewById(R.id.camera);
if (mCameraView != null) {
mCameraView.addCallback(mCallback);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* 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 com.google.android.cameraview;

import org.hamcrest.Description;
import org.hamcrest.Factory;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;

import static org.hamcrest.CoreMatchers.either;

public class AspectRatioIsCloseTo extends TypeSafeMatcher<AspectRatio> {

private final AspectRatio mRatio;
private final static float ERROR = 0.01f;

public AspectRatioIsCloseTo(AspectRatio ratio) {
mRatio = ratio;
}

@Override
protected boolean matchesSafely(AspectRatio item) {
float other = item.toFloat();
float self = mRatio.toFloat();
return self - ERROR < other && other < self + ERROR;
}

@Override
public void describeTo(Description description) {
description.appendText("an aspect ratio of ").appendValue(mRatio.toString());
}

@Factory
public static Matcher<AspectRatio> closeTo(AspectRatio ratio) {
return new AspectRatioIsCloseTo(ratio);
}

@Factory
public static Matcher<AspectRatio> closeToOrInverse(AspectRatio ratio) {
return either(closeTo(ratio)).or(closeTo(ratio.inverse()));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@
import static android.support.test.espresso.matcher.ViewMatchers.assertThat;
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static com.google.android.cameraview.AspectRatioIsCloseTo.closeToOrInverse;
import static junit.framework.Assert.assertFalse;
import static org.hamcrest.CoreMatchers.either;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.hasItem;
import static org.hamcrest.CoreMatchers.is;
Expand Down Expand Up @@ -178,9 +178,26 @@ public void perform(UiController uiController, View view) {
public void check(View view, NoMatchingViewException noViewFoundException) {
CameraView cameraView = (CameraView) view;
AspectRatio cameraRatio = cameraView.getAspectRatio();
AspectRatio viewRatio = new AspectRatio(view.getWidth(), view.getHeight());
assertThat(cameraRatio, is(either(equalTo(viewRatio))
.or(equalTo(viewRatio.inverse()))));
AspectRatio viewRatio = AspectRatio.of(view.getWidth(), view.getHeight());
assertThat(cameraRatio, is(closeToOrInverse(viewRatio)));
}
});
}

@Test
public void testTextureViewSize() {
onView(withId(R.id.camera))
.check(new ViewAssertion() {
@Override
public void check(View view, NoMatchingViewException noViewFoundException) {
CameraView cameraView = (CameraView) view;
TextureView textureView = (TextureView)
view.findViewById(R.id.texture_view);
AspectRatio cameraRatio = cameraView.getAspectRatio();
assert cameraRatio != null;
AspectRatio textureRatio = AspectRatio.of(
textureView.getWidth(), textureView.getHeight());
assertThat(textureRatio, is(closeToOrInverse(cameraRatio)));
}
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,26 +17,51 @@
package com.google.android.cameraview;

import android.support.annotation.NonNull;
import android.support.v4.util.SparseArrayCompat;

/**
* Immutable class for describing proportional relationship between width and height.
*/
public class AspectRatio implements Comparable<AspectRatio> {

private final static SparseArrayCompat<SparseArrayCompat<AspectRatio>> sCache
= new SparseArrayCompat<>(16);

private final int mX;
private final int mY;

/**
* Creates a new instance of {@link AspectRatio}. The values {@code x} and {@code} will be
* reduced by their greatest common divider.
* Returns an instance of {@link AspectRatio} specified by {@code x} and {@code y} values.
* The values {@code x} and {@code} will be reduced by their greatest common divider.
*
* @param x The width
* @param y The height
* @return An instance of {@link AspectRatio}
*/
public AspectRatio(int x, int y) {
public static AspectRatio of(int x, int y) {
int gcd = gcd(x, y);
mX = x / gcd;
mY = y / gcd;
x /= gcd;
y /= gcd;
SparseArrayCompat<AspectRatio> arrayX = sCache.get(x);
if (arrayX == null) {
AspectRatio ratio = new AspectRatio(x, y);
arrayX = new SparseArrayCompat<>();
arrayX.put(y, ratio);
sCache.put(x, arrayX);
return ratio;
} else {
AspectRatio ratio = arrayX.get(y);
if (ratio == null) {
ratio = new AspectRatio(x, y);
arrayX.put(y, ratio);
}
return ratio;
}
}

private AspectRatio(int x, int y) {
mX = x;
mY = y;
}

public int getX() {
Expand Down Expand Up @@ -99,7 +124,7 @@ public int compareTo(@NonNull AspectRatio another) {
*/
public AspectRatio inverse() {
//noinspection SuspiciousNameCombination
return new AspectRatio(mY, mX);
return AspectRatio.of(mY, mX);
}

private static int gcd(int a, int b) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ public CameraViewImpl(Callback callback) {

abstract AspectRatio getAspectRatio();

abstract int getDisplayOrientation();

interface Callback {

void onCameraOpened();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public boolean add(Size size) {
// None of the existing ratio matches the provided size; add a new key
List<Size> sizes = new ArrayList<>();
sizes.add(size);
mRatios.put(new AspectRatio(size.getWidth(), size.getHeight()), sizes);
mRatios.put(AspectRatio.of(size.getWidth(), size.getHeight()), sizes);
return true;
}

Expand Down
37 changes: 25 additions & 12 deletions library/src/main/camera1/com/google/android/cameraview/Camera1.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import android.content.Context;
import android.graphics.SurfaceTexture;
import android.hardware.Camera;
import android.util.Log;
import android.view.Surface;
import android.view.TextureView;
import android.view.WindowManager;
Expand All @@ -31,7 +32,8 @@ class Camera1 extends CameraViewImpl {

private static final int INVALID_CAMERA_ID = -1;

private static final AspectRatio DEFAULT_ASPECT_RATIO = new AspectRatio(4, 3);
private static final AspectRatio DEFAULT_ASPECT_RATIO = AspectRatio.of(4, 3);
private static final String TAG = "Camera1";

private final Context mContext;

Expand All @@ -49,6 +51,8 @@ class Camera1 extends CameraViewImpl {

private AspectRatio mAspectRatio;

private int mDisplayOrientation;

private static class PreviewInfo {
SurfaceTexture surface;
int width;
Expand Down Expand Up @@ -120,8 +124,9 @@ void startPreview() {

private void setUpPreview() {
try {
mCamera.setPreviewTexture(mPreviewInfo.surface);
mCamera.setDisplayOrientation(calcDisplayOrientation());
if (mPreviewInfo.surface != null) {
mCamera.setPreviewTexture(mPreviewInfo.surface);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
Expand Down Expand Up @@ -165,6 +170,11 @@ AspectRatio getAspectRatio() {
return mAspectRatio;
}

@Override
int getDisplayOrientation() {
return mDisplayOrientation;
}

/**
* This rewrites {@link #mCameraId} and {@link #mCameraInfo}.
*/
Expand Down Expand Up @@ -193,17 +203,19 @@ private void openCamera() {
}
// AspectRatio
if (mAspectRatio == null) {
mAspectRatio = DEFAULT_ASPECT_RATIO;
}
final List<Size> sizes = mPreviewSizes.sizes(mAspectRatio);
if (sizes == null) { // Not supported
mAspectRatio = chooseAspectRatio();
} else {
final List<Size> sizes = mPreviewSizes.sizes(mAspectRatio);
if (sizes == null) { // Not supported
mAspectRatio = chooseAspectRatio();
} else { // The specified AspectRatio is supported
Size size = chooseOptimalSize(sizes);
mCameraParameters.setPreviewSize(size.getWidth(), size.getHeight());
mCamera.setParameters(mCameraParameters);
}
}
Size size = chooseOptimalSize(sizes);
mCameraParameters.setPreviewSize(size.getWidth(), size.getHeight());
Log.d(TAG, "openCamera: " + size.getWidth() + "x" + size.getHeight());
mCamera.setParameters(mCameraParameters);
// Display orientation
mDisplayOrientation = calcDisplayOrientation();
mCamera.setDisplayOrientation(mDisplayOrientation);
mCallback.onCameraOpened();
}

Expand All @@ -219,6 +231,7 @@ private AspectRatio chooseAspectRatio() {
}

private Size chooseOptimalSize(List<Size> sizes) {
Log.d(TAG, "chooseOptimalSize: " + sizes.get(0));
return sizes.get(0); // TODO: Pick optimally
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ public class CameraView extends FrameLayout {

private boolean mAdjustViewBounds;

private TextureView mTextureView;

public CameraView(Context context) {
this(context, null);
}
Expand All @@ -57,8 +59,8 @@ public CameraView(Context context, AttributeSet attrs, int defStyleAttr) {
}
// View content
inflate(context, R.layout.camera_view, this);
TextureView textureView = (TextureView) findViewById(R.id.texture_view);
textureView.setSurfaceTextureListener(mImpl.getSurfaceTextureListener());
mTextureView = (TextureView) findViewById(R.id.texture_view);
mTextureView.setSurfaceTextureListener(mImpl.getSurfaceTextureListener());
// Attributes
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CameraView, defStyleAttr,
R.style.Widget_CameraView);
Expand All @@ -68,6 +70,7 @@ public CameraView(Context context, AttributeSet attrs, int defStyleAttr) {

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// Handle android:adjustViewBounds
if (mAdjustViewBounds) {
if (!isCameraOpened()) {
mCallbacks.reserveRequestLayoutOnOpen();
Expand Down Expand Up @@ -100,6 +103,26 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
} else {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
// Measure the TextureView
int width = getMeasuredWidth();
int height = getMeasuredHeight();
AspectRatio ratio = getAspectRatio();
int orientation = mImpl.getDisplayOrientation();
if (orientation == 90 || orientation == 270) {
ratio = ratio.inverse();
}
assert ratio != null;
if (height < width * ratio.getY() / ratio.getX()) {
mTextureView.measure(
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(width * ratio.getY() / ratio.getX(),
MeasureSpec.EXACTLY));
} else {
mTextureView.measure(
MeasureSpec.makeMeasureSpec(height * ratio.getX() / ratio.getY(),
MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
}
}

/**
Expand Down Expand Up @@ -179,7 +202,7 @@ public boolean getAdjustViewBounds() {
/**
* Sets the aspect ratio of camera.
*
* @param ratio The {@AspectRatio} to be set.
* @param ratio The {@link AspectRatio} to be set.
*/
public void setAspectRatio(@NonNull AspectRatio ratio) {
mImpl.setAspectRatio(ratio);
Expand Down
3 changes: 2 additions & 1 deletion library/src/main/res/layout/activity_camera_view.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
<com.google.android.cameraview.CameraView
android:id="@+id/camera"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
android:layout_height="match_parent"
android:keepScreenOn="true"/>

</FrameLayout>
Loading

0 comments on commit 129e2d9

Please sign in to comment.