I have a dialog design where camera preview should be landscape in portrait mode (see pics). I choose preview size correctly through getOptimalPreviewSize() and when I don't use setDisplayOrientation(90) I get this:
When I use setDisplayOrientation(90) I get this:
Does anybody have any ideas how to fix it? Can android do that kind of thing?
Answer (thanks to Eddy Talvala):
Due to you can't process the whole cam picture in portrait when your cam view in xml is horizontal, you should crop. I decided to fit horizontally and crop all that in the bottom:
| |
| _________ | _____
|| || | |
|| || | ^-^ |
|| ______ || | \_/ |
|| | ^-^ ||| | | |
|| |_\_/_ ||| |__|__|
|| image || sensor
|| ||
|| ||
||_________||
|| < O [] ||
|___________|
device
You should use TextureView to specify matrix for fit and cropping. Something like that (kotlin):
fun camInit() {
val camParam = camera.parameters
val optimalSize = getOptimalPortraitPreviewSize(camParam.supportedPreviewSizes, width, height)
camParam.setPreviewSize(optimalSize.width, optimalSize.height)
camera.parameters = camParam
camera.setPreviewTexture(surface)
val scaleDelta = optimalSize.height - preview.width // for portrait
val scaleY: Float = (optimalSize.width.toFloat() - scaleDelta) / preview.height // for portrait
val matrix = Matrix()
matrix.setScale(1f, scaleY) // 1f cause we fit horizontally
preview.setTransform(matrix)
camera.startPreview()
}
Notice that for portrait mode you should use specific getOptimalProtraitPreviewSize(), cause standard func that everybody uses to get camera preview optimal size (with ASPECT_TOLERANCE and other things) can return you a size with a small resolution. Try something like this:
fun getOptimalPortraitPreviewSize(sizes: List<Camera.Size>, previewWidth: Int) {
var selectedSize: Camera.Size = sizes[0]
for (size in sizes) {
// use size's height cause of portrait
val currentSizeWidthDelta = abs(size.height - previewWidth)
val selectedSizeWidthDelta = abs(selectedSize.height - previewWidth)
if (currentSizeWidthDelta < selectedSizeWidthDelta) selectedSize = size
}
return selectedSize
}
The long edge of the image sensor lines up with the long edge of the device.
(bad ascii art):
____________
| |
| _________ | _____
|| || | |
|| || | |
|| || | |
|| || | |
|| || |_____|
|| || sensor
|| || physical
|| || orientation
||_________||
|| < O [] ||
|___________|
device
Given that, there's no way you can draw the image like this:
_____________
| |
| _________ | _____
|| || | |
|| || | |
|| ______ || | |
|| | ||| | |
|| |______||| |_____|
|| image || sensor
|| ||
|| ||
||_________||
|| < O [] ||
|___________|
device
unless you either rotate the image (in which case up isn't up):
_____________
| |
| _________ | _____
|| || | |
|| || | ^-^ |
|| ______ || | \_/ |
|| | <-\__||| | | |
|| |_<-/__||| |__|__|
|| image || sensor
|| ||
|| ||
||_________||
|| < O [] ||
|___________|
device
or you stretch the image horizontally, which looks pretty bad:
_____________
| |
| _________ | _____
|| || | |
|| || | ^-^ |
|| ______ || | \_/ |
|| | ^___^||| | | |
|| |___|__||| |__|__|
|| image || sensor
|| ||
|| ||
||_________||
|| < O [] ||
|___________|
device
or you crop a slice of the image, which reduces the FOV a lot:
_____________
| |
| _________ | _____
|| || | |
|| || | ^-^ |
|| ______ || | \_/ |
|| | ^-^ ||| | | |
|| |_\_/__||| |__|__|
|| image || sensor
|| ||
|| ||
||_________||
|| < O [] ||
|___________|
device
Because the image sensor is landscape when the phone is landscape, there's no way to place a landscape preview in a portrait UI without one of those three things happening. You either need a portrait preview in a portrait UI, or you need to crop the image down. That's not a limitation of Android, it's just a limitation of geometry.
If you want to crop, you'll probably want to send the camera data to a SurfaceTexture, and crop in OpenGL, if you want a live preview.