parent
f844040a29
commit
f92c5b1e37
@ -0,0 +1,125 @@ |
|||||||
|
package com.otaliastudios.cameraview.engine.offset; |
||||||
|
|
||||||
|
import androidx.annotation.NonNull; |
||||||
|
|
||||||
|
import com.otaliastudios.cameraview.controls.Facing; |
||||||
|
|
||||||
|
public class Angles { |
||||||
|
|
||||||
|
private Facing mSensorFacing; |
||||||
|
private int mSensorOffset = 0; |
||||||
|
private int mDisplayOffset = 0; |
||||||
|
private int mDeviceOrientation = 0; |
||||||
|
|
||||||
|
/** |
||||||
|
* We want to keep everything in the {@link Axis#ABSOLUTE} reference, |
||||||
|
* so a front facing sensor offset must be inverted. |
||||||
|
* |
||||||
|
* @param sensorFacing sensor facing value |
||||||
|
* @param sensorOffset sensor offset |
||||||
|
*/ |
||||||
|
public void setSensorOffset(@NonNull Facing sensorFacing, int sensorOffset) { |
||||||
|
sanitizeInput(sensorOffset); |
||||||
|
mSensorFacing = sensorFacing; |
||||||
|
mSensorOffset = sensorOffset; |
||||||
|
if (mSensorFacing == Facing.FRONT) { |
||||||
|
mSensorOffset = sanitizeOutput(360 - mSensorOffset); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the display offset. |
||||||
|
* @param displayOffset the display offset |
||||||
|
*/ |
||||||
|
public void setDisplayOffset(int displayOffset) { |
||||||
|
sanitizeInput(displayOffset); |
||||||
|
mDisplayOffset = displayOffset; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the device orientation. |
||||||
|
* @param deviceOrientation the device orientation |
||||||
|
*/ |
||||||
|
public void setDeviceOrientation(int deviceOrientation) { |
||||||
|
sanitizeInput(deviceOrientation); |
||||||
|
mDeviceOrientation = deviceOrientation; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the offset between two reference systems, computed along the given axis. |
||||||
|
* @param from the source reference system |
||||||
|
* @param to the destination reference system |
||||||
|
* @param axis the axis |
||||||
|
* @return the offset |
||||||
|
*/ |
||||||
|
public int offset(@NonNull Reference from, @NonNull Reference to, @NonNull Axis axis) { |
||||||
|
int offset = absoluteOffset(from, to); |
||||||
|
if (axis == Axis.RELATIVE_TO_SENSOR) { |
||||||
|
if (mSensorFacing == Facing.FRONT) { |
||||||
|
offset = sanitizeOutput(360 - offset); |
||||||
|
} |
||||||
|
} |
||||||
|
return offset; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Old results: |
||||||
|
* offset(REF_SENSOR, REF_OUTPUT, Facing.BACK) = mSensorOffset + mDeviceOrientation |
||||||
|
* offset(REF_SENSOR, REF_OUTPUT, Facing.FRONT) = -mSensorOffset - mDeviceOrientation |
||||||
|
* offset(REF_SENSOR, REF_VIEW, Facing.BACK) = mSensorOffset - mDisplayOffset |
||||||
|
* offset(REF_SENSOR, REF_VIEW, Facing.FRONT) = mSensorOffset - mDisplayOffset |
||||||
|
* offset(REF_OUTPUT, REF_VIEW, Facing.BACK) = (mSensorOffset - mDisplayOffset) - (mSensorOffset + mDeviceOrientation) = -mDisplayOffset - mDeviceOrientation |
||||||
|
* offset(REF_OUTPUT, REF_VIEW, Facing.FRONT) = (mSensorOffset - mDisplayOffset) - (-mSensorOffset - mDeviceOrientation) = 2*mSensorOffset + mDisplayOffset + mDeviceOrientation |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* New results: |
||||||
|
* offset(REF_SENSOR, REF_OUTPUT) = mDeviceOrientation + mSensorOffset |
||||||
|
* -> REF_SENSOR, REF_OUTPUT is typically wanted in the RELATIVE_TO_SENSOR axis. |
||||||
|
* |
||||||
|
* offset(REF_SENSOR, REF_VIEW) = -mDisplayOffset + mSensorOffset |
||||||
|
* -> REF_SENSOR, REF_VIEW is typically wanted in the ABSOLUTE axis. |
||||||
|
*/ |
||||||
|
|
||||||
|
private int absoluteOffset(@NonNull Reference from, @NonNull Reference to) { |
||||||
|
if (from == to) { |
||||||
|
return 0; |
||||||
|
} else if (to == Reference.BASE) { |
||||||
|
return 360 - absoluteOffset(to, from); |
||||||
|
} else if (from == Reference.BASE) { |
||||||
|
switch (to) { |
||||||
|
case VIEW: return sanitizeOutput(360 - mDisplayOffset); |
||||||
|
case OUTPUT: return sanitizeOutput(mDeviceOrientation); |
||||||
|
case SENSOR: return sanitizeOutput(360 - mSensorOffset); |
||||||
|
default: throw new RuntimeException("Unknown reference: " + to); |
||||||
|
} |
||||||
|
} else { |
||||||
|
return sanitizeOutput( |
||||||
|
absoluteOffset(Reference.BASE, to) |
||||||
|
- absoluteOffset(Reference.BASE, from)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Whether the two references systems are flipped. |
||||||
|
* @param from source |
||||||
|
* @param to destination |
||||||
|
* @return true if flipped |
||||||
|
*/ |
||||||
|
public boolean flip(@NonNull Reference from, @NonNull Reference to) { |
||||||
|
return offset(from, to, Axis.ABSOLUTE) % 180 != 0; |
||||||
|
} |
||||||
|
|
||||||
|
private void sanitizeInput(int value) { |
||||||
|
if (value != 0 |
||||||
|
&& value != 90 |
||||||
|
&& value != 180 |
||||||
|
&& value != 270) { |
||||||
|
throw new IllegalStateException("This value is not sanitized: " + value); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private int sanitizeOutput(int value) { |
||||||
|
return (value + 360) % 360; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,28 @@ |
|||||||
|
package com.otaliastudios.cameraview.engine.offset; |
||||||
|
|
||||||
|
/** |
||||||
|
* The axis around which offsets are computed. We have two possibilities: |
||||||
|
* - an axis going out of the device screen towards the user's face |
||||||
|
* - an axis going out of the device screen towards the back |
||||||
|
* |
||||||
|
* We are mostly interested in the first one, but some APIs will require |
||||||
|
* angles in the sensor reference, in which case, for front cameras, we are |
||||||
|
* required to use {@link #RELATIVE_TO_SENSOR}. |
||||||
|
*/ |
||||||
|
public enum Axis { |
||||||
|
|
||||||
|
/** |
||||||
|
* This rotation axis is the one going out of the device screen |
||||||
|
* towards the user's face. |
||||||
|
*/ |
||||||
|
ABSOLUTE, |
||||||
|
|
||||||
|
/** |
||||||
|
* This rotation axis takes into account the current |
||||||
|
* {@link com.otaliastudios.cameraview.controls.Facing} value. |
||||||
|
* |
||||||
|
* - for {@link com.otaliastudios.cameraview.controls.Facing#BACK}, this equals {@link #ABSOLUTE} |
||||||
|
* - for {@link com.otaliastudios.cameraview.controls.Facing#FRONT}, this is inverted |
||||||
|
*/ |
||||||
|
RELATIVE_TO_SENSOR |
||||||
|
} |
@ -0,0 +1,28 @@ |
|||||||
|
package com.otaliastudios.cameraview.engine.offset; |
||||||
|
|
||||||
|
public enum Reference { |
||||||
|
|
||||||
|
/** |
||||||
|
* The base reference system has its 'north' aligned with the device natural |
||||||
|
* orientation. |
||||||
|
*/ |
||||||
|
BASE, |
||||||
|
|
||||||
|
/** |
||||||
|
* This reference system has its 'north' aligned with the camera sensor. |
||||||
|
*/ |
||||||
|
SENSOR, |
||||||
|
|
||||||
|
/** |
||||||
|
* This reference system has its 'north' aligned with the View hierarchy. |
||||||
|
* This can be different than {@link #BASE} if the activity is allowed to rotate |
||||||
|
* (or forced into a non natural position). |
||||||
|
*/ |
||||||
|
VIEW, |
||||||
|
|
||||||
|
/** |
||||||
|
* This reference system has its 'north' aligned with the output picture/video. |
||||||
|
* This means that it takes into account the device orientation. |
||||||
|
*/ |
||||||
|
OUTPUT |
||||||
|
} |
Loading…
Reference in new issue