42

I want to show portrait orientation on Zxing's camera.

How can this be done?

Roy Lee
  • 10,572
  • 13
  • 60
  • 84

9 Answers9

108

Here's how it works.

Step 1: Add following lines to rotate data before buildLuminanceSource(..) in decode(byte[] data, int width, int height)

DecodeHandler.java:

byte[] rotatedData = new byte[data.length];
for (int y = 0; y < height; y++) {
    for (int x = 0; x < width; x++)
        rotatedData[x * height + height - y - 1] = data[x + y * width];
}
int tmp = width;
width = height;
height = tmp;

PlanarYUVLuminanceSource source = activity.getCameraManager().buildLuminanceSource(rotatedData, width, height);

Step 2: Modify getFramingRectInPreview().

CameraManager.java

rect.left = rect.left * cameraResolution.y / screenResolution.x;
rect.right = rect.right * cameraResolution.y / screenResolution.x;
rect.top = rect.top * cameraResolution.x / screenResolution.y;
rect.bottom = rect.bottom * cameraResolution.x / screenResolution.y;

Step 3: Disable the check for Landscape Mode in initFromCameraParameters(...)

CameraConfigurationManager.java

//remove the following
if (width < height) {
  Log.i(TAG, "Display reports portrait orientation; assuming this is incorrect");
  int temp = width;
  width = height;
  height = temp;
}

Step 4: Add following line to rotate camera insetDesiredCameraParameters(...)

CameraConfigurationManager.java

camera.setDisplayOrientation(90);

Step 5: Do not forget to set orientation of activity to portrait. I.e: manifest

Roy Lee
  • 10,572
  • 13
  • 60
  • 84
  • 5
    This is pretty good! although you're going from only supporting landscape to only supporting portrait, rather than both. This won't work for front cameras either, or the upside-down portrait orientation. – Sean Owen Apr 27 '13 at 14:13
  • @SeanOwen Yes sir, you are right :) It just a very simple tweak on the library to have a portrait-only camera scanning. – Roy Lee Apr 27 '13 at 14:43
  • for me it doesn't works. My view finder rectangle in landScape mode but the camera rotate in 180. what goes wrong? – Shihab Uddin Jun 02 '13 at 14:35
  • The camera images are squashed/not in proportion after going portrait. It scans fine though. Any way to fix this? – happy_iphone_developer Jun 13 '13 at 16:09
  • 1
    this exception shows FATAL EXCEPTION: Thread-9 java.lang.IllegalArgumentException: Crop rectangle does not fit within image data. : at com.google.zxing.client.android.PlanarYUVLuminanceSource.(PlanarYUVLuminanceSource.java:46) : at com.google.zxing.client.android.camera.CameraManager.buildLuminanceSource(CameraManager.java:275) at com.google.zxing.client.android.DecodeHandler.decode(DecodeHandler.java:80) E/AndroidRuntime(5523): at com.google.zxing.client.android.DecodeHandler.handleMessage(DecodeHandler.java:54) – Rohit Jul 13 '13 at 10:47
  • @Rohit How is it going now? DId you follow all the stesps? – Roy Lee Jul 14 '13 at 03:30
  • 1
    @Roylee , hey thnks , it is working, i want to ask, can it work in both landscap and portrait according to sensor, please help if u can – Rohit Jul 15 '13 at 04:22
  • @Rohit Do you meant if you could have both landscape and portrait? Not with this code snippet :) – Roy Lee Jul 16 '13 at 07:30
  • @Rohit I haven't tried yet and I didn't touched this project for quite sometime. Are you able to find a solution in SO, maybe you could have opened a thread? – Roy Lee Jul 18 '13 at 10:04
  • This rotates the camera etc but then it doesnt scan for me. – Russell Cargill Aug 14 '13 at 12:50
  • @RussellCargill Are you following the steps correctly? it works for others. – Roy Lee Aug 15 '13 at 02:13
  • 1
    Yeah I followed them to the letter but it would never scan for me :S I found a solution though. Don't rotate it haha! I'm using it for QR Codes so it works both ways so I shall just remove any text from the view and leave it at that! But thanks for the concern. – Russell Cargill Aug 15 '13 at 09:04
  • Great to hear that :) – Roy Lee Aug 15 '13 at 09:11
  • 1
    worked to me, although camera image is a bit out of aspect ratio (qrcode, nexus4) – Lucas Jota Jan 02 '14 at 16:20
  • I´m having problems when running in ldpi device (Samsung Galaxy Y GT-S5360), the camera image stays wrong... did anyone experienced this? – Lucas Jota Jan 02 '14 at 19:50
  • But qr code is not being scanned proper as landscape mode – Hiren Patel Apr 24 '14 at 04:28
  • 1
    This is outdated with current version of ZXing library – Warpzit Jun 26 '14 at 11:59
  • 1
    If it doers not scan after rotate, I suspect step 1 is not followed PlanarYUVLuminanceSource source = activity.getCameraManager().buildLuminanceSource(rotatedData, width, height); Remember to replace data with "rotatedData". The text at step 1 is a bit misleading. One could think to only add the code, not replace the data variable. – Glenn Bech Aug 14 '14 at 12:55
  • 1
    Very helpful, since I'm still holding on to ZXing v2.2 for legacy compatibility. I made it auto-detect the orientation -- instead of hard-coding portrait mode, I verify that the orientation is Configuration.ORIENTATION_PORTRAIT before performing these alternate operations. I also implemented a rotation calculation method as described in the comments for Camera.setDisplayOrientation(). Now I can rotate at will and the image appears correct (and it scans). – ProjectJourneyman Aug 19 '14 at 07:06
  • 1
    @Warpzit i just tried the 3.1.0 version, and couldn't see any convenient way to achieve portrait mode for barcodes – Suau Sep 09 '14 at 11:02
  • @Su-AuHwang Exacly, gotta work with legacy libs or set down spending hours to reverse engineer =/ – Warpzit Sep 09 '14 at 12:11
  • @Warpzit what I meant was: I don't see how this answer is outdated. It still works on 3.10 and 3.10 still doesn't offer a more convenient way. – Suau Sep 10 '14 at 03:49
  • @Su-AuHwang Okay, I might've done something wrong, at least this fix didn't work for me with current version of ZXing library. The library has also been changed a lot since this was made. – Warpzit Sep 10 '14 at 09:20
  • my zxing version is 3.0.1, and the above changes seem do not apply to my case. I cannot have the camera in portrait mode. any helps? TIA – Derekyy Sep 19 '14 at 04:40
  • May I ask, for Step4, where should I put the code in? – Derekyy Sep 19 '14 at 04:43
  • @Derekyy CameraConfigurationManager.java – Roy Lee Sep 19 '14 at 10:12
  • It works finally, but how could I change the dimension of the small rectangle inside the barcode scanner (the non-shady region)? – Derekyy Sep 23 '14 at 02:29
  • The image captured by camera is rotated by 90 degree while i am trying to scan. Like if you are clicking a photo of a person using the camera, then in my phone screen is showing the preview rotated by 90 degree. But that is not what i want as it is making bar code scanning difficult to use. I want preview as it should be. 1) So 1st of all is it like that what you guys are getting it work too or i have done something wrong? 2) If it is like so is there any way to make it normal user friendly? – Rohit Goyal Nov 03 '14 at 10:45
  • need to fix findBestPreviewSizeValue() method in CameraConfigurationManager.java, too: find int maybeFlippedWidth = isCandidatePortrait ? realHeight : realWidth; int maybeFlippedHeight = isCandidatePortrait ? realWidth : realHeight; and edit to: int maybeFlippedHeight = isCandidatePortrait ? realHeight : realWidth; int maybeFlippedWidth = isCandidatePortrait ? realWidth : realHeight; – yelliver Nov 22 '14 at 15:23
  • If someone's looking for supporting both portrait and landscape mode, check this answer : http://stackoverflow.com/a/29504069/2128979 – Ashish Tanna Apr 08 '15 at 00:57
  • Hi, did you idea for zxing new version lib (3.0). it work with old version – Sameer Z. May 13 '15 at 09:31
  • @SameerZ. Unfortunately, not at the moment. It's been awhile since my last programming with zxing lib. Why not find out yourself and then post it here, if its correct, I will definitely mark it as answer :) – Roy Lee May 13 '15 at 15:10
  • 1
    @Roylee https://github.com/xiaowei4895/zxing-android-portrait finally it is working – Sameer Z. May 14 '15 at 10:01
  • works well, but the yellow result points are not shown on the barcode. – mustafa May 28 '15 at 13:53
  • Great. Very well explained in steps – jrh Feb 07 '18 at 09:55
7

To support all orientation and change automatically when rotating the activity do this, all you have to modify is the CameraManager.javaclass.

And remove this method getCurrentOrientation() from CaptureActivity.java

In CameraManager.java Create this variable:

int resultOrientation;

Add this to the openDriver(..) method:

setCameraDisplayOrientation(context, Camera.CameraInfo.CAMERA_FACING_BACK, theCamera);//this can be set after camera.setPreviewDisplay(); in api13+.

****Create this method**** Link: http://developer.android.com/reference/android/hardware/Camera.html

public static void setCameraDisplayOrientation(Context context,int cameraId, android.hardware.Camera camera) {
    android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
    android.hardware.Camera.getCameraInfo(cameraId, info);
    Display display = ((WindowManager)context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
    int degrees = 0;
    switch (display.getRotation()) {
    case Surface.ROTATION_0: degrees = 0; break;
    case Surface.ROTATION_90: degrees = 90; break;
    case Surface.ROTATION_180: degrees = 180; break;
    case Surface.ROTATION_270: degrees = 270; break;
    }


    if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
        resultOrientation = (info.orientation + degrees) % 360;
        resultOrientation = (360 - resultOrientation) % 360;  // compensate the mirror
    } else {  // back-facing
        resultOrientation = (info.orientation - degrees + 360) % 360;
    }
    camera.setDisplayOrientation(resultOrientation);
}

****Now modify getFramingRectInPreview()****

if(resultOrientation == 180 || resultOrientation == 0){//to work with landScape and reverse landScape
            rect.left = rect.left * cameraResolution.x / screenResolution.x;
            rect.right = rect.right * cameraResolution.x / screenResolution.x;
            rect.top = rect.top * cameraResolution.y / screenResolution.y;
            rect.bottom = rect.bottom * cameraResolution.y / screenResolution.y;
        }else{
            rect.left = rect.left * cameraResolution.y / screenResolution.x;
            rect.right = rect.right * cameraResolution.y / screenResolution.x;
            rect.top = rect.top * cameraResolution.x / screenResolution.y;
            rect.bottom = rect.bottom * cameraResolution.x / screenResolution.y;
        }

And modify this method public PlanarYUVLuminanceSource buildLuminanceSource(..)

if(resultOrientation == 180 || resultOrientation == 0){//TODO: This is to use camera in landScape mode
        // Go ahead and assume it's YUV rather than die.
        return new PlanarYUVLuminanceSource(data, width, height, rect.left, rect.top, rect.width(), rect.height(), false);
    }else{
        byte[] rotatedData = new byte[data.length];
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++)
                rotatedData[x * height + height - y - 1] = data[x + y * width];
        }
        int tmp = width;
        width = height;
        height = tmp;
        return new PlanarYUVLuminanceSource(rotatedData, width, height, rect.left, rect.top, rect.width(), rect.height(), false);
    }
Rensodarwin
  • 286
  • 4
  • 12
5

You can use my fork of zxlib https://github.com/rusfearuth/zxing-lib-without-landscape-only. I disabled landscape mode only. You can set landscape/portrait and see correct camera view.

Rusfearuth
  • 3,261
  • 5
  • 28
  • 37
3

Adding camera.setDisplayOrientation(90); in CameraConfigurationManager.java worked for me.

Damian Kozlak
  • 7,065
  • 10
  • 45
  • 51
2

for zxing 3.0, working lib https://github.com/xiaowei4895/zxing-android-portrait for portrait mode

Thank you

Sameer Z.
  • 3,265
  • 9
  • 48
  • 72
2

I think the best library only solution is this one ...

https://github.com/SudarAbisheck/ZXing-Orient

You can include it in build.gradle as a dependency of your project in maven format ...

dependencies {
  compile ''me.sudar:zxing-orient:2.1.1@aar''
}
dodgy_coder
  • 12,407
  • 10
  • 54
  • 67
  • 1
    This library does not work. Jsut does not scan, all I get is `QRCodeNotFoundOnCamImage` – Siddharth Jun 12 '16 at 16:03
  • @Siddharth thanks yes I've edited my answer for a different library that works well, in fact the same way as ZXing BarcodeScanner and BarcodeScannerPlus apps. – dodgy_coder Jun 29 '16 at 05:51
2

Create AnyOrientationCaptureActivity and then override default CaptureActivity then it will work.

public void scanCode() {
    IntentIntegrator integrator = new IntentIntegrator(this);
    integrator.setDesiredBarcodeFormats(CommonUtil.POSEIDON_CODE_TYPES);
    integrator.setPrompt("Scan");
    integrator.setCameraId(0);
    integrator.setBeepEnabled(false);
    integrator.setBarcodeImageEnabled(false);
    integrator.setOrientationLocked(false);
    //Override here
    integrator.setCaptureActivity(AnyOrientationCaptureActivity.class);

    integrator.initiateScan();
}

//create AnyOrientationCaptureActivity extend CaptureActivity
public class AnyOrientationCaptureActivity extends CaptureActivity {
}

Define in manifest

<activity
            android:name=".views.AnyOrientationCaptureActivity"
            android:screenOrientation="fullSensor"
            android:stateNotNeeded="true"
            android:theme="@style/zxing_CaptureTheme"
            android:windowSoftInputMode="stateAlwaysHidden"></activity>
Nathan Tuggy
  • 2,237
  • 27
  • 30
  • 38
LinhNguyen
  • 21
  • 2
1

This is supposed to be a synched version to the above solution

https://github.com/zxing/zxing/tree/4b124b109d90ac2960078ce68e15a39885fc1b5b

sivi
  • 10,654
  • 2
  • 52
  • 51
1

Additionally to @roylee's modification I had to apply the following to the CameraConfigurationManager.java in order to get best possible preview and QR code recognition quality

    diff --git a/android/src/com/google/zxing/client/android/camera/CameraConfigurationManager.java b/android/src/com/google/zxing/client/android/camera/CameraConfigurationManager.java
index cd9d0d8..4f12c8c 100644
--- a/android/src/com/google/zxing/client/android/camera/CameraConfigurationManager.java
+++ b/android/src/com/google/zxing/client/android/camera/CameraConfigurationManager.java
@@ -56,21 +56,24 @@ public final class CameraConfigurationManager {
     Display display = manager.getDefaultDisplay();
     int width = display.getWidth();
     int height = display.getHeight();
-    // We're landscape-only, and have apparently seen issues with display thinking it's portrait 
+    // We're landscape-only, and have apparently seen issues with display thinking it's portrait
     // when waking from sleep. If it's not landscape, assume it's mistaken and reverse them:
+    /*
     if (width < height) {
       Log.i(TAG, "Display reports portrait orientation; assuming this is incorrect");
       int temp = width;
       width = height;
       height = temp;
     }
+    */
     screenResolution = new Point(width, height);
     Log.i(TAG, "Screen resolution: " + screenResolution);
-    cameraResolution = findBestPreviewSizeValue(parameters, screenResolution, false);
+    cameraResolution = findBestPreviewSizeValue(parameters, screenResolution, true);//
     Log.i(TAG, "Camera resolution: " + cameraResolution);
   }

   void setDesiredCameraParameters(Camera camera) {
+    camera.setDisplayOrientation(90);
     Camera.Parameters parameters = camera.getParameters();

     if (parameters == null) {
@@ -99,7 +102,7 @@ public final class CameraConfigurationManager {
   Point getScreenResolution() {
     return screenResolution;
   }
-  
+
   public void setFrontCamera(boolean newSetting) {
     SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
     boolean currentSetting = prefs.getBoolean(PreferencesActivity.KEY_FRONT_CAMERA, false);
@@ -109,12 +112,12 @@ public final class CameraConfigurationManager {
       editor.commit();
     }
   }
-  
+
   public boolean getFrontCamera() {
     SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
     return prefs.getBoolean(PreferencesActivity.KEY_FRONT_CAMERA, false);
   }
-  
+
   public boolean getTorch() {
     SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
     return prefs.getBoolean(PreferencesActivity.KEY_FRONT_LIGHT, false);
@@ -181,7 +184,14 @@ public final class CameraConfigurationManager {
       Camera.Size defaultSize = parameters.getPreviewSize();
       bestSize = new Point(defaultSize.width, defaultSize.height);
     }
+
+    // FIXME: test the bestSize == null case!
+    // swap width and height in portrait case back again
+    if (portrait) {
+        bestSize = new Point(bestSize.y, bestSize.x);
+    }
     return bestSize;
+
   }

   private static String findSettableValue(Collection<String> supportedValues,
simne7
  • 194
  • 1
  • 1
  • 12