can somebody help me out with the following code. I want to transform the HdrViewFinder project from google samples (https://github.com/googlesamples/android-HdrViewfinder) so that it also works in portrait mode. But the project works only in landscape mode.
In portrait mode, the camera preview gets distorted.
So, here is the method I extracted from the HdrViewfinderActivity.java class which I think is responsible for setting the whole view into landscape:
/**
* Configure the surfaceview and RS processing.
*/
private void configureSurfaces() {
// Find a good size for output - largest 16:9 aspect ratio that's less than 720p
final int MAX_WIDTH = 1280;
final float TARGET_ASPECT = 16.f / 9.f;
final float ASPECT_TOLERANCE = 0.1f;
StreamConfigurationMap configs =
mCameraInfo.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
if (configs == null) {
throw new RuntimeException("Cannot get available picture/preview sizes.");
}
Size[] outputSizes = configs.getOutputSizes(SurfaceHolder.class);
Size outputSize = outputSizes[0];
float outputAspect = (float) outputSize.getWidth() / outputSize.getHeight();
for (Size candidateSize : outputSizes) {
if (candidateSize.getWidth() > MAX_WIDTH) continue;
float candidateAspect = (float) candidateSize.getWidth() / candidateSize.getHeight();
boolean goodCandidateAspect =
Math.abs(candidateAspect - TARGET_ASPECT) < ASPECT_TOLERANCE;
boolean goodOutputAspect =
Math.abs(outputAspect - TARGET_ASPECT) < ASPECT_TOLERANCE;
if ((goodCandidateAspect && !goodOutputAspect) ||
candidateSize.getWidth() > outputSize.getWidth()) {
outputSize = candidateSize;
outputAspect = candidateAspect;
}
}
Log.i(TAG, "Resolution chosen: " + outputSize);
// Configure processing
mProcessor = new ViewfinderProcessor(mRS, outputSize);
setupProcessor();
// Configure the output view - this will fire surfaceChanged
mPreviewView.setAspectRatio(outputAspect);
mPreviewView.getHolder().setFixedSize(outputSize.getWidth(), outputSize.getHeight());
}
How could I change this method so that it works also in portrait mode? What do I need to change ?
Setting only the screenOrientation attribute in the manifest file to portrait did not help.
I have found out the 16:9 is used for full landscape mode and that 9:16 used for full portrait mode. So, I changed MAX_WIDTH to 720 and TARGET_ASPECT to 9.f/16.f . But that also did not help.
Can somebody provide a solution ?
Here is a little sketch showing the problem/what happens when I just set the screenOrientation attribute in the manifest file to portrait mode:
I have an experimental fork that achieves this, by switching to TextureView from SurfaceView.
tested on Sony G8441 a.k.a Xperia XZ1 Compact with Android Pie
Related
Following are the screenshots when using texture view in camera2 apis.In full screen the preview stretches,but it works when using lower resolution(second image).
How to use this preview in full screen without stretching it.
Below answer assumes you are in portrait mode only.
Your question is
How to use the preview in full-screen without stretching it
Let's break it down to 2 things:
You want the preview to fill the screen
The preview cannot be distorted
First you need to know that this is logically impossible without crop, if your device's viewport has a different aspect ratio with any available resolution the camera provides.
So I would assume you accept cropping the preview.
Step 1: Get a list of available resolutions
StreamConfigurationMap map = mCameraCharacteristics.get(
CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
if (map == null) {
throw new IllegalStateException("Failed to get configuration map: " + mCameraId);
}
Size[] sizes = map.getOutputSizes(SurfaceTexture.class);
Now you get a list of available resolutions (Sizes) of your device's camera.
Step 2: Find the best aspect ratio
The idea is to loop the sizes and see which one best fits. You probably need to write your own implementation of "best fits".
I am not going to provide any code here since what I have is quite different from your use case. But ideally, it should be something like this:
Size findBestSize (Size[] sizes) {
//Logic goes here
}
Step 3: Tell the Camera API that you want to use this size
//...
textureView.setBufferSize(bestSize.getWidth(), bestSize.getHeight());
Surface surface = textureView.getSurface();
try {
mPreviewRequestBuilder = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
mPreviewRequestBuilder.addTarget(surface);
mCamera.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()),
mSessionCallback, null);
} catch (final Exception e) {
//...
}
Step 4: Make your preview extends beyond your viewport
This is then nothing related to the Camera2 API. We "crop" the preview by letting the SurfaceView / TextureView extends beyond device's viewport.
First place your SurfaceView or TextureView in a RelativeLayout.
Use the below to extend it beyond the screen, after you get the aspect ratio from step 2.
Note that in this case you probably need to know this aspect ratio before you even start the camera.
//Suppose this value is obtained from Step 2.
//I simply test here by hardcoding a 3:4 aspect ratio, where my phone has a thinner aspect ratio.
float cameraAspectRatio = (float) 0.75;
//Preparation
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
int screenWidth = metrics.widthPixels;
int screenHeight = metrics.heightPixels;
int finalWidth = screenWidth;
int finalHeight = screenHeight;
int widthDifference = 0;
int heightDifference = 0;
float screenAspectRatio = (float) screenWidth / screenHeight;
//Determines whether we crop width or crop height
if (screenAspectRatio > cameraAspectRatio) { //Keep width crop height
finalHeight = (int) (screenWidth / cameraAspectRatio);
heightDifference = finalHeight - screenHeight;
} else { //Keep height crop width
finalWidth = (int) (screenHeight * cameraAspectRatio);
widthDifference = finalWidth - screenWidth;
}
//Apply the result to the Preview
RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) cameraView.getLayoutParams();
lp.width = finalWidth;
lp.height = finalHeight;
//Below 2 lines are to center the preview, since cropping default occurs at the right and bottom
lp.leftMargin = - (widthDifference / 2);
lp.topMargin = - (heightDifference / 2);
cameraView.setLayoutParams(lp);
If you don't care about the result of Step 2, you can actually ignore Step 1 to Step 3 and simply use a library out there, as long as you can configure its aspect ratio. (It looks like this one is the best, but I haven't tried yet)
I have tested using my forked library. Without modifying any code of my library, I managed to make the preview fullscreen just by using Step 4:
Before using Step 4:
After using Step 4:
And the preview just after taking a photo will not distort as well, because the preview is also extending beyond your screen.
But the output image will include area that you cannot see in the preview, which makes perfect sense.
The code of Step 1 to Step 3 are generally referenced from Google's CameraView.
That's a common problem on some devices. I've noticed it mostly on samsung. You may use a trick with setting transformation on your TextureView to make it centerCrop like ImageView behaviour
I also faced similar situation, but this one line solved my problem
view_finder.preferredImplementationMode = PreviewView.ImplementationMode.TEXTURE_VIEW
in your xml:
<androidx.camera.view.PreviewView
android:id="#+id/view_finder"
android:layout_width="match_parent"
android:layout_height="match_parent" />
For camera implementation using cameraX you can refer
https://github.com/android/camera-samples/tree/master/CameraXBasic
I figured out what was your poroblem. You were probably trying something like this:
textureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
#Override
public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int i, int j) {
cam.startPreview(surfaceTexture, i, j);
cam.takePicture();
}
public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int i, int i1) { }
public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) { return false; }
public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) { }
});
Device Nexus 4
Android ver: 4.2.2
Hoping someone else has found this and can explain how to resolve it....
Nexus 4 supports the following preview sizes: -
W:1280 H:720 Ratio: 1.777777
W:800 H:480 Ratio: 1.6666666
W:768 H:432 Ratio: 1.7777778
W:720 H:480 Ratio: 1.5
W:640 H:480 Ratio: 1.3333334
W:576 H:432 Ratio: 1.3333334
W:480 H:320 Ratio: 1.5
W:384 H:288 Ratio: 1.3333334
W:352 H:288 Ratio: 1.2222222
W:320 H:240 Ratio: 1.3333334
W:240 H:160 Ratio: 1.5
W:176 H:144 Ratio: 1.2222222
myCamera.setPreviewSize() sets them, and when I call myCamera.getPreviewSize() I get the correct one that I set, BUT.... If I set my surface view to the same size as my camera preview then I get a stretched image. e.g.
setPreviewSize(640,480)
getPreviewSize -> I get 640,480
Surface view (640,480) -> Stretched image
Only if I set my Surface view to 16x9 (1.77777) do I get a perfect image.
Surface view (1280,720) -> Perfect image
This is the only device that I have this issue with. Please can someone advise if there is a camera setting I am missing for full screen mode or something that is stopping this from working.
In my long search I found 1 other post which relates also to this issue, but not an answer, just a bit more evidence of my problem
PictureCallback not called unless using supportedsizes[0]
Thanks
On the Nexus 4, there's an unfortunate issue where the preview aspect ratio and the still picture aspect ratio are tied together, even if you never take a picture. If they don't match, one of the two gets distorted (typically preview, since it is the lower resolution).
If you want to use a 4:3 preview, configure the still picture size to be 4:3 as well, before starting preview. For example, 640x480 preview with a full 8MP picture size (3264 x 2448) on N4 should cause no stretching for preview.
Use Camera.Parameters.setPictureSize to select the picture size; the list of available sizes can be read from Camera.Parameters.getSupportedPictureSizes.
This method calculate the best (for me) screen size for each device. But, I have the same problem like you when I tried this code in the Nexus 4. So, my solution is to have a special case in the end of this method which gets the width of the nexus 4 and calculates the best height for this device.
The last case could be used in all devices. You could delete the first part of the method.
private void setAspectResolutionCamera(Parameters camParams, int screen_width, int screen_height) {
boolean chosen_one_resolution = false;
//Init screen sizes
width_video = ConstantsCamera.VIDEO_ASPECT_WIDTH;
height_video = ConstantsCamera.VIDEO_ASPECT_HEIGHT;
float aspect_ratio = 1f;
int aspect_width = 6000, aspect_height = 6000;
List<Size> supported_sizes_list = camParams.getSupportedPreviewSizes();
for (int i = 0; i < supported_sizes_list.size(); i++) {
Size size = supported_sizes_list.get(i);
float aspect = (float) size.height / size.width;
if (ConstantsCamera.VIDEO_ASPECT_RATIO - aspect <= aspect_ratio && (aspect - ConstantsCamera.VIDEO_ASPECT_RATIO >= 0)) {
if (screen_width - size.height <= aspect_width && size.height - screen_width >= 0) {
if (screen_height - size.width < aspect_height) {
height_video = size.width;
width_video = size.height;
aspect_ratio = ConstantsCamera.VIDEO_ASPECT_RATIO - (float) size.height / size.width;
aspect_width = screen_width - size.height;
aspect_height = screen_height - size.width;
chosen_one_resolution = true;
}
}
}
}
//Special case
if (width_video != screen_width && !chosen_one_resolution) {
height_video = screen_width * height_video / width_video;
width_video = screen_width;
}
}
Try setting the size of your surfaceview based on the ratio of the camera parameters that you are using.
I have made android build using cocos2dx for the device having size 480*320 and it works fine but when i put the same build in another android device having size 640*480 the scaling issue occurs....
I am using following code to re-size automatically, but it is not working:
AppDelegate app;
CCEGLView& eglView = CCEGLView::sharedOpenGLView();
eglView.setViewName("Hello Lua");
eglView.setFrameSize(480, 320);
// set the design resolution screen size, if you want to use Design Resoulution scaled to current screen, please uncomment next line.
// eglView.setDesignResolutionSize(480, 320);
First of all, the code you have pasted is from main.cpp file which does not play a part when you compile the app for android or windows phone or ios. That file is for win32 project.
Now,
for automatic scaling of app, the function in AppDelegate.cpp should set design resolution as well as policy. I use ExactFit policy because it stretches the app to fill the whole screen area and it works on Android , Windows, iOS everything (I have developed apps and tested) :
bool AppDelegate::applicationDidFinishLaunching()
{
// initialize director
CCDirector *pDirector = CCDirector::sharedDirector();
pDirector->setOpenGLView(CCEGLView::sharedOpenGLView());
// turn on display FPS
CCEGLView::sharedOpenGLView()->setDesignResolutionSize(1200, 720, kResolutionExactFit);
pDirector->setDisplayStats(false);
pDirector->setAnimationInterval(1.0 / 60);
CCScene *pScene = HelloWorld::scene();
pDirector->runWithScene(pScene);
return true;
}
You should also take a look at this Detailed explanation of multi-resolution support to understand it better.
If you are not conscious about stretched look then do this. It will work on all the devices.
pDirector->setOpenGLView(pEGLView);
CCSize frameSize = pDirector->getOpenGLView()->getFrameSize();
CCLog("%.2f and %.2f is Frame size\n",frameSize.width,frameSize.height);
pEGLView->setDesignResolutionSize(960, 640, kResolutionExactFit);
Note: The above setting is for Landscape, if you need it for portrait just reverse the values like pEGLView->setDesignResolutionSize(640, 960, kResolutionExactFit)
The thing to understand here is that everything you will design on the coordinates of 640X960 and kResolutionExactFit will fix the things as Fit to the Screen. To do this more easily you should use Coocos Builder http://cocosbuilder.com/ and design everything on a custom layout of 640X960
I am recommending double resolution (640X960) because this way Quality won't be reduced and will look crisp on all kind of devices, however the drawback of Stretched Look would be there.
If you want to consider multiple resolutions in multiple way then you have to make multiple graphics and multiple Layouts, again Cocos Builder would be the best way to do this.
i.e for iPhone 5 it would be diff and for iPad and standard 480X320 it would be diff. To do this you can put checks on frame size to select respective View.
CCSize frameSize = pDirector->getOpenGLView()->getFrameSize();
You should experience some cutting at each side. This is done because is how best the compatibility mode match the screen res of 480x320. The others option are leaving some part of the height out or stretch the image (at cost of some performance (?) ).
The first can be easy done one the CCEGLView::create method, you can change the MIN to MAX in the scalefactor formula and its done, but this probably will breake all y position of your sprites :P
bool AppDelegate::applicationDidFinishLaunching()
{
// initialize director
CCDirector* pDirector = CCDirector::sharedDirector();
CCEGLView* pEGLView = CCEGLView::sharedOpenGLView();
pDirector->setOpenGLView(pEGLView);
// Set the design resolution
// iPad size
CCPoint res = CCPoint(768,1024);
pEGLView->setDesignResolutionSize(res.x,
res.y,
kResolutionNoBorder);
.....
}
Check the resolution and make changes for screen size:
(This example is a portrait game)
typedef struct tagResource
{
cocos2d::Size size;
char directory[100];
}Resource;
std::vector<std::string> searchPath;
static Resource designResource = { cocos2d::Size(768, 1024), "ipad"};
static Resource smallResource = { cocos2d::Size(320, 480), "iphone"};
static Resource galax7Resource = { cocos2d::Size(600, 1024), "gt_7p" };
static Resource mediumResource = { cocos2d::Size(768, 1024), "ipad"};
static Resource galax10Resource = { cocos2d::Size(800, 1280), "gt_10p"};
static Resource fullHDResource = { cocos2d::Size(1080, 1920), "fullhd"};
static Resource largeResource = { cocos2d::Size(1536, 2048), "ipadhd"};
auto director = Director::getInstance();
auto glview = director->getOpenGLView();
glview->setDesignResolutionSize(designResource.size.width, designResource.size.height, ResolutionPolicy::EXACT_FIT);
Size frameSize = glview->getFrameSize();
Resource realResource;
// if the frame's height is larger than the height of medium size.
if (frameSize.height > fullHDResource.size.height) {
realResource = largeResource;
CCLOG("largeResource");
} else if (frameSize.height > galax10Resource.size.height) {
realResource = fullHDResource;
CCLOG("fullHDResource");
} else if (frameSize.height > mediumResource.size.height) {
realResource = galax10Resource;
CCLOG("galax10Resource");
} else if (frameSize.height > smallResource.size.height) {
if(frameSize.width > galax7Resource.size.width){
realResource = mediumResource;
CCLOG("mediumResource");
}else {
realResource = galax7Resource;
CCLOG("galax7Resource");
}
} else {
realResource = smallResource;
CCLOG("smallResource");
}
director->setContentScaleFactor(MIN(realResource.size.height/designResource.size.height, realResource.size.width/designResource.size.width));
searchPath.push_back(realResource.directory);
auto fileUtils = FileUtils::getInstance();
fileUtils->setSearchPaths(searchPath);
I'm using cocos2dx V3 but the idea is the same for V2.
I have used Following resolution policy in my 3match game
In AppDelegate.cpp
#define IS_LANDSCAPE
#ifdef IS_LANDSCAPE
static cocos2d::Size designResolutionSize = cocos2d::Size(1136,768);
#else
static cocos2d::Size designResolutionSize = cocos2d::Size(768,1136);
#endif
//==========================
bool AppDelegate::applicationDidFinishLaunching() {
// initialize director
auto director = Director::getInstance();
auto glview = director->getOpenGLView();
if(!glview) {
glview = GLViewImpl::create("My Game");
director->setOpenGLView(glview);
}
// turn on display FPS
director->setDisplayStats(false);
// set FPS. the default value is 1.0/60 if you don't call this
director->setAnimationInterval(1.0 / 60);
// Set the design resolution
glview->setDesignResolutionSize(designResolutionSize.width, designResolutionSize.height, ResolutionPolicy::NO_BORDER);
register_all_packages();
// create a scene. it's an autorelease object
auto scene =loading::createScene();
// run
director->runWithScene(scene);
return true;
}
This question already has answers here:
Zxing Camera in Portrait mode on Android
(9 answers)
Closed 9 years ago.
i've succeeded using the ZXing barcode scanning library , but only on landscape mode.
i've also succeeded setting the camera preview to be on portrait mode AND show it correctly (without stretching) , but now the barcode doesn't work at all .
here're the changes i've made to "setDesiredCameraParameters" on "CameraConfigurationManager.java" in order to show the camera correctly :
void setDesiredCameraParameters(Camera camera)
{
Camera.Parameters parameters = camera.getParameters();
Log.d(TAG, "Setting preview size: " + cameraResolution);
setFlash(parameters);
setZoom(parameters);
camera.setDisplayOrientation(90);
parameters.set("rotation", 90);
parameters.setPreviewSize(cameraResolution.y, cameraResolution.x);
camera.setParameters(parameters);
}
i've tried some solutions mentioned on other places , but either they don't work , or they work but cannot show the camera preview correctly .
examples:
How to use Zxing in portrait mode?
http://code.google.com/p/zxing/issues/detail?id=178#c46
https://github.com/pplante/zxing-android/issues
when i'm finished with that , i also need to customize the location&size of the rectangle to the scanning . i know that i need to change "setManualFramingRect" on "CameraManager.java" , but i'm not sure if i do it correctly . here's the code for that:
public void setManualFramingRect(Rect rect)
{
if (initialized)
{
Point screenResolution = configManager.getScreenResolution();
if (rect.right >= screenResolution.x)
rect.right = screenResolution.x - 1;
if (rect.left < 0)
rect.left = 0;
if (rect.bottom >= screenResolution.y)
rect.bottom = screenResolution.y - 1;
if (rect.top < 0)
rect.top = 0;
framingRect = rect;
Log.d(TAG, "Calculated manual framing rect: " + framingRect);
framingRectInPreview = null;
}
else
_requestedFramingRect = new Rect(rect);
}
of course ,i've changed "openDriver" to call :
if (_requestedFramingRect != null)
setManualFramingRect(_requestedFramingRect);
please help me.
EDIT: now i've found out that it doesn't work on some devices . it crashes on the beginning , and if you debug , you can see that even the preview doesn't work well.
There's more to it than this. For example you need to actually "rotate" the camera data (or, scan it as if it's vertical) when the camera orientation is not the same as the device orientation. And when using a front camera, you need to account for the fact that its rotation is reversed.
Hi guys,
I am developing an application for both tablets as well as mobile,Till now every thing is fine to me. Here is my problem,I can't get the height and width of Samsung Galaxy Tab?
I want what is configuration for 7 inch tablet i have declared as
config.orientation == Configuration.SCREENLAYOUT_SIZE_LARGE. It is not working for me.
if else how i need to declare this.
here i have declared i n the layout as layout-large it's working for me, but i am having both design for landscape and protrait.
Try this:
DisplayMetrics dm = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(dm);
String strScreenSize = "The Android Screen is: "
+ dm.widthPixels
+ " x "
+ dm.heightPixels;
If you need the size classification — ”small”, “normal”, “large”, “xlarge” — use the Configuration class:
Configuration config = getResources().getConfiguration();
int size = config.screenLayout & config.SCREENLAYOUT_SIZE_MASK;
if (size == config.SCREENLAYOUT_SIZE_SMALL)
{
// use special layout for small screens
}
If you need check for a orientation then you can use:
public int getScreenOrientation()
{
Display getOrient = getWindowManager().getDefaultDisplay();
int orientation = Configuration.ORIENTATION_UNDEFINED;
if(getOrient.getWidth() == getOrient.getHeight()) {
orientation = Configuration.ORIENTATION_SQUARE;
} else {
if(getOrient.getWidth() < getOrient.getHeight()){
orientation = Configuration.ORIENTATION_PORTRAIT;
} else {
orientation = Configuration.ORIENTATION_LANDSCAPE;
}
}
return orientation;
}
I don't really get it, why you need check for a screen size, because if you add your layouts in different layout folders, then Android automatically selects most suitable depending on screen size.
There are several websites that fetch this kind of info, without you having to write scripts.
e.g. http://www.screenresolution.org/