Added the quality test for exposure compensation.

- Users have the choice of run all the tests or run tests for a range.
- The tests for a certain range will loop through all the values of
  supported exposure compensation and draw the response curve.

Change-Id: Id330732fd5401bfe871b3c148c032c3247571cd9
diff --git a/apps/CtsVerifier/include/colorchecker/exposurecompensationtest.h b/apps/CtsVerifier/include/colorchecker/exposurecompensationtest.h
new file mode 100644
index 0000000..866b25e
--- /dev/null
+++ b/apps/CtsVerifier/include/colorchecker/exposurecompensationtest.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+#ifndef EXPOSURECOMPENSATIONTEST_H
+#define EXPOSURECOMPENSATIONTEST_H
+
+#include <vector>
+
+#include "imagetesthandler.h"
+
+class ExposureCompensationTest : public ImageTestHandler {
+  public:
+    ExposureCompensationTest() : ImageTestHandler() {
+      initializeReferenceColors();
+    }
+    ExposureCompensationTest(int debugHeight, int debugWidth) :
+        ImageTestHandler(debugHeight, debugWidth) {
+      initializeReferenceColors();
+    }
+    ~ExposureCompensationTest() {}
+
+    void addDataToList(const float exposureValue,
+                  const std::vector<Vec3f>* checkerColors) {
+      mExposureValues.push_back(exposureValue);
+      mCheckerColors.push_back(*checkerColors);
+      delete checkerColors;
+    }
+
+    void processData();
+
+  private:
+    void initializeReferenceColors();
+
+    std::vector<std::vector<Vec3f> > mCheckerColors;
+    std::vector<Vec3i> mReferenceColors;
+    std::vector<float> mExposureValues;
+};
+
+#endif
diff --git a/apps/CtsVerifier/jni/cameraanalyzer/Android.mk b/apps/CtsVerifier/jni/cameraanalyzer/Android.mk
index 06e075e..cac9ab3 100644
--- a/apps/CtsVerifier/jni/cameraanalyzer/Android.mk
+++ b/apps/CtsVerifier/jni/cameraanalyzer/Android.mk
@@ -24,10 +24,10 @@
 
 LOCAL_SRC_FILES := com_android_cts_verifier_camera_analyzer_CameraTests.cpp \
                 com_android_cts_verifier_camera_analyzer_ColorCheckerTest.cpp \
+                com_android_cts_verifier_camera_analyzer_ExposureCompensationTest.cpp \
                 com_android_cts_verifier_camera_analyzer_AutoLockTest.cpp \
                 com_android_cts_verifier_camera_analyzer_MeteringTest.cpp \
-                #com_android_cts_verifier_camera_analyzer_ExposureCompensationTest.cpp \
-                com_android_cts_verifier_camera_analyzer_WhiteBalanceTest.cpp
+                #com_android_cts_verifier_camera_analyzer_WhiteBalanceTest.cpp
 
 LOCAL_C_INCLUDES += $(LOCAL_PATH)/../../include/colorchecker $(JNI_H_INCLUDE)
 
diff --git a/apps/CtsVerifier/jni/cameraanalyzer/com_android_cts_verifier_camera_analyzer_ExposureCompensationTest.cpp b/apps/CtsVerifier/jni/cameraanalyzer/com_android_cts_verifier_camera_analyzer_ExposureCompensationTest.cpp
new file mode 100644
index 0000000..68c8256
--- /dev/null
+++ b/apps/CtsVerifier/jni/cameraanalyzer/com_android_cts_verifier_camera_analyzer_ExposureCompensationTest.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+#define LOG_NDEBUG 0
+
+#define LOG_TAG "ExposureCompensationJNI"
+#include <utils/Log.h>
+#include <vector>
+#include <string.h>
+
+#include "android/bitmap.h"
+#include "testingimage.h"
+#include "exposurecompensationtest.h"
+#include "vec2.h"
+
+#include "com_android_cts_verifier_camera_analyzer_ExposureCompensationTest.h"
+
+jlong Java_com_android_cts_verifier_camera_analyzer_ExposureCompensationTest_createExposureCompensationTest(
+      JNIEnv*      env,
+      jobject      thiz,
+      jint         debugHeight,
+      jint         debugWidth) {
+
+    ExposureCompensationTest* testHandler =
+            new ExposureCompensationTest(debugHeight, debugWidth);
+    long handlerAddress = (long)testHandler;
+
+    return handlerAddress;
+}
+
+void Java_com_android_cts_verifier_camera_analyzer_ExposureCompensationTest_createExposureCompensationClass(
+        JNIEnv*      env,
+        jobject      thiz,
+        jlong        inputImageAddress,
+        jlong        inputHandlerAddress,
+        jlong        checkerCenterAddress,
+        jlong        checkerRadiusAddress,
+        jfloat       exposureValue) {
+
+    ALOGV("JNI createExposureCompensationClass starts!");
+
+    long imageAddress = (long)inputImageAddress;
+    long handlerAddress = (long)inputHandlerAddress;
+
+    TestingImage *inputImage = (TestingImage*) imageAddress;
+    ExposureCompensationTest *testHandler =
+            (ExposureCompensationTest*) handlerAddress;
+
+    std::vector<std::vector< Vec2f > >* checkerCenter =
+            (std::vector<std::vector< Vec2f > >*) (long) checkerCenterAddress;
+    std::vector<std::vector< float > >* checkerRadius =
+            (std::vector<std::vector< float > >*) (long) checkerRadiusAddress;
+
+    const std::vector<Vec3f>* checkerValue =
+            inputImage->getColorChecker(3, 4, 0, 6,
+                                        checkerCenter, checkerRadius);
+    testHandler->addDataToList((float) exposureValue, checkerValue);
+    delete inputImage;
+}
+
+void Java_com_android_cts_verifier_camera_analyzer_ExposureCompensationTest_processExposureCompensationTest(
+        JNIEnv*      env,
+        jobject      thiz,
+        jlong        inputHandlerAddress) {
+
+    long handlerAddress = (long) inputHandlerAddress;
+    ExposureCompensationTest *testHandler =
+            (ExposureCompensationTest*) handlerAddress;
+
+    testHandler->processData();
+}
diff --git a/apps/CtsVerifier/jni/cameraanalyzer/com_android_cts_verifier_camera_analyzer_ExposureCompensationTest.h b/apps/CtsVerifier/jni/cameraanalyzer/com_android_cts_verifier_camera_analyzer_ExposureCompensationTest.h
new file mode 100644
index 0000000..c77f0e4
--- /dev/null
+++ b/apps/CtsVerifier/jni/cameraanalyzer/com_android_cts_verifier_camera_analyzer_ExposureCompensationTest.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+#ifndef JNI_CAMERAANALYZER_EXPOSURECOMPENSATIONTEST_H
+#define JNI_CAMERAANALYZER_EXPOSURECOMPENSATIONTEST_H
+
+#include <jni.h>
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+JNIEXPORT jlong JNICALL
+Java_com_android_cts_verifier_camera_analyzer_ExposureCompensationTest_createExposureCompensationTest(
+        JNIEnv*      env,
+        jobject      thiz,
+        jint         debugHeight,
+        jint         debugWidth);
+
+JNIEXPORT void JNICALL
+Java_com_android_cts_verifier_camera_analyzer_ExposureCompensationTest_createExposureCompensationClass(
+        JNIEnv*      env,
+        jobject      thiz,
+        jlong        inputImageAddress,
+        jlong        inputHandlerAddress,
+        jlong        checkerCenterAddress,
+        jlong        checkerRadiusAddress,
+        jfloat       exposureValue);
+
+JNIEXPORT void JNICALL
+Java_com_android_cts_verifier_camera_analyzer_ExposureCompensationTest_processExposureCompensationTest(
+        JNIEnv*      env,
+        jobject      thiz,
+        jlong        inputHandlerAddress);
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/apps/CtsVerifier/lib/colorchecker/Android.mk b/apps/CtsVerifier/lib/colorchecker/Android.mk
index cdebfae..d76e639 100644
--- a/apps/CtsVerifier/lib/colorchecker/Android.mk
+++ b/apps/CtsVerifier/lib/colorchecker/Android.mk
@@ -29,10 +29,10 @@
                    vec2.cpp \
                    imagetesthandler.cpp \
                    colorcheckertest.cpp \
+                   exposurecompensationtest.cpp \
                    autolocktest.cpp \
-                   meteringtest.cpp \
+                   meteringtest.cpp
                    #whitebalancetest.cpp \
-                   exposurecompensationtest.cpp
 
 LOCAL_C_INCLUDES += $(LOCAL_PATH)/../../include/colorchecker
 LOCAL_SHARED_LIBRARIES := libstlport \
diff --git a/apps/CtsVerifier/lib/colorchecker/exposurecompensationtest.cpp b/apps/CtsVerifier/lib/colorchecker/exposurecompensationtest.cpp
new file mode 100644
index 0000000..3e58141
--- /dev/null
+++ b/apps/CtsVerifier/lib/colorchecker/exposurecompensationtest.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+#define LOG_NDEBUG 0
+
+#define LOG_TAG "ExposureCompensationTest"
+#include <utils/Log.h>
+#include <utils/Timers.h>
+#include <cmath>
+#include <string>
+#include <stdio.h>
+
+#include "vec2.h"
+#include "vec3.h"
+#include "exposurecompensationtest.h"
+
+const float GAMMA_CORRECTION = 2.2f;
+void ExposureCompensationTest::processData() {
+    ALOGV("Start Processing Exposure Compensation Test Data!");
+    clearDebugImage();
+
+    int numTests = mExposureValues.size();
+    int numPatches = mCheckerColors[0].size();
+    ALOGV("Processing %d tests with %d patches", numTests, numPatches);
+
+    float minExposure = -3.0f;
+    float scale = 9.0f;
+    for (int i = 0; i < numTests; ++i) {
+        for (int j = 0; j < numPatches; ++j) {
+            int exposureRed = static_cast<int>((
+                log(static_cast<float>(mReferenceColors[j].r()))
+                / log(2.0f) * GAMMA_CORRECTION +
+                mExposureValues[i] - minExposure) * scale);
+            int exposureGreen = static_cast<int>((
+                log(static_cast<float>(mReferenceColors[j].g()))
+                / log(2.0f) * GAMMA_CORRECTION +
+                mExposureValues[i] - minExposure) * scale);
+            int exposureBlue = static_cast<int>((
+                log(static_cast<float>(mReferenceColors[j].b()))
+                / log(2.0f) * GAMMA_CORRECTION +
+                mExposureValues[i] - minExposure) * scale);
+
+            ALOGV("Match color at (%d, %d) is (%f, %f, %f)", i, j,
+                 mCheckerColors[i][j].r(),
+                 mCheckerColors[i][j].g(),
+                 mCheckerColors[i][j].b());
+            ALOGV("Response curve red (%d, %f), blue (%d, %f), green (%d, %f)",
+                 exposureRed, mCheckerColors[i][j].r(),
+                 exposureGreen, mCheckerColors[i][j].g(),
+                 exposureBlue, mCheckerColors[i][j].b());
+
+            Vec3i red(255, 0, 0);
+            Vec3i green(0, 255, 0);
+            Vec3i blue(0, 0, 255);
+
+            drawPoint(200 - exposureRed, mCheckerColors[i][j].r(), red);
+            drawPoint(200 - exposureGreen, mCheckerColors[i][j].g(), green);
+            drawPoint(200 - exposureBlue, mCheckerColors[i][j].b(), blue);
+        }
+    }
+    mExposureValues.clear();
+    mCheckerColors.clear();
+}
+
+void ExposureCompensationTest::initializeReferenceColors() {
+    mReferenceColors.resize(6);
+
+    mReferenceColors[0].set(243, 243, 242);
+    mReferenceColors[1].set(200, 200, 200);
+    mReferenceColors[2].set(160, 160, 160);
+    mReferenceColors[3].set(122, 122, 121);
+    mReferenceColors[4].set(85, 85, 85);
+    mReferenceColors[5].set(52, 52, 52);
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/analyzer/CameraAnalyzerActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/analyzer/CameraAnalyzerActivity.java
index aa12476..b4c7dbd 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/analyzer/CameraAnalyzerActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/analyzer/CameraAnalyzerActivity.java
@@ -209,7 +209,7 @@
 
         ColorCheckerTest.getSingletonTest().updateCamera();
         //WhiteBalanceTest.getSingletonTest().updateCamera();
-        //ExposureCompensationTest.getSingletonTest().updateCamera();
+        ExposureCompensationTest.getSingletonTest().updateCamera();
         MeteringTest.getSingletonTest().updateCamera();
         AutoLockTest.getSingletonTest().updateCamera();
     }
@@ -320,16 +320,16 @@
         public void onClick(View v) {
             Log.v(TAG, "Running new exposure compensation tests!");
 
-            /*ExposureCompensationTest exposureCompensationTest =
+            ExposureCompensationTest exposureCompensationTest =
                     ExposureCompensationTest.getSingletonTest();
 
             mCurrentTest = exposureCompensationTest;
-            initializeAdapter();*/
+            initializeAdapter();
 
             // Loads the memory address of the checker centers and radius
             // from the this class and set the two values for the new test.
-            //ExposureCompensationTest.setCheckerAddress(mCheckerCenterAddress,
-            //                                       mCheckerRadiusAddress);
+            ExposureCompensationTest.setCheckerAddress(mCheckerCenterAddress,
+                                                   mCheckerRadiusAddress);
         }
     };
 
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/analyzer/ExposureCompensationTest.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/analyzer/ExposureCompensationTest.java
new file mode 100644
index 0000000..828840d
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/analyzer/ExposureCompensationTest.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2011 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.android.cts.verifier.camera.analyzer;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.ImageFormat;
+import android.hardware.Camera;
+import android.util.Log;
+import android.widget.ImageView;
+
+import java.io.FileOutputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.Random;
+
+public class ExposureCompensationTest extends CameraTests {
+
+    private static final String TAG = "ExposureCompensationTest";
+
+    private float mExposureLevel;
+    private final Object mProcessingImage = new Object();
+    private final Object mAutoFocusing = new Object();
+    private long mTestHandler;
+    private String[] mTestResults;
+    private int mNumTests;
+    private Camera.Parameters mParams;
+
+    private static ExposureCompensationTest singletonTest = null;
+
+    private ExposureCompensationTest(){
+        super();
+    }
+
+    public void updateCamera() {
+        mParams = mTestCamera.getParameters();
+        Log.v(TAG, String.format("Exposure level is from %d to %d",
+                                 mParams.getMinExposureCompensation(),
+                                 mParams.getMaxExposureCompensation()));
+        mNumTests = (int) ((float) (mParams.getMaxExposureCompensation() -
+                                    mParams.getMinExposureCompensation())
+                            * mParams.getExposureCompensationStep());
+        mTestResults = new String[mNumTests + 1];
+        for (int i = 0; i < mNumTests + 1; ++i) {
+            mTestResults[i] = "...";
+        }
+    }
+
+    public static synchronized ExposureCompensationTest getSingletonTest() {
+        if (singletonTest == null) {
+            Log.v(TAG, "Creating a new ExposureCompensationTest instance");
+            singletonTest = new ExposureCompensationTest();
+            singletonTest.initializeTest();
+        }
+        return singletonTest;
+    }
+
+    private void initializeTest() {
+        // Creates a native test handler with a 120x160 pixel debug output
+        mTestHandler = createExposureCompensationTest(200, 280);
+    }
+
+    @Override
+    public synchronized void run(int index){
+        Log.v(TAG, "ExposureCompensationTest thread started!");
+
+        int testRangeMin, testRangeMax;
+        if (index == 0) {
+            testRangeMin = mParams.getMinExposureCompensation();
+            testRangeMax = mParams.getMaxExposureCompensation();
+        } else {
+            testRangeMin = (int) ((float)(index - 1) / mParams.getExposureCompensationStep())
+                    + mParams.getMinExposureCompensation();
+            testRangeMax = (int) ((float)(index) / mParams.getExposureCompensationStep())
+                    + mParams.getMinExposureCompensation();
+        }
+
+        for (int i = testRangeMin;
+                i <= testRangeMax; i += 1){
+            mExposureLevel = i * mParams.getExposureCompensationStep();
+            Log.v(TAG, String.format("Current exposure level is %d", i));
+            int mCameraExposure;
+
+            do{
+                mParams.setExposureCompensation(i);
+                mTestCamera.setParameters(mParams);
+
+                try{
+                    Log.v(TAG, "Waiting");
+                    Thread.sleep(4000);
+                    Log.v(TAG, "END Waiting");
+                } catch (InterruptedException e){
+                    //TODO: error handling.
+                }
+
+                mParams = mTestCamera.getParameters();
+                mCameraExposure = mParams.getExposureCompensation();
+                Log.v(TAG, String.format("Camera exposure level is %d", mCameraExposure));
+            } while (mCameraExposure != i);
+
+            mTestCamera.takePicture(null, null, null, mTestJpegListener);
+
+            synchronized (mProcessingImage) {
+                try{
+                    Log.v(TAG, "Start waiting for Image");
+                    mProcessingImage.wait();
+                } catch (InterruptedException e){
+                    Log.v(TAG, "Callback wait fails!");
+                }
+            }
+        }
+        processExposureCompensationTest(mTestHandler);
+        displayHandlerDebugOutput(mTestHandler);
+
+        Log.v(TAG, "Callback has returned!");
+        mParams.setExposureCompensation(0);
+        mTestCamera.setParameters(mParams);
+    }
+
+    private Camera.PictureCallback mTestJpegListener = new Camera.PictureCallback() {
+        public void onPictureTaken(byte[] data, Camera mCamera) {
+            Log.v(TAG, "Shutter pressed down!");
+            Log.v(TAG, String.format("Current exposure is %f", mExposureLevel));
+
+            try {
+                FileOutputStream outStream = new FileOutputStream(
+                    String.format("/sdcard/exposure%d.jpg", System.currentTimeMillis()));
+                outStream.write(data);
+                outStream.close();
+            } catch (FileNotFoundException e) {
+            } catch (IOException e) {}
+
+            Bitmap inputImage;
+
+            inputImage = BitmapFactory.decodeByteArray(data, 0, data.length);
+            long bufferAddress = findNative(inputImage);
+            Log.v(TAG, "findNative method finishes");
+
+            inputImage.recycle();
+            data = null;
+            inputImage = null;
+            System.gc();
+
+            createExposureCompensationClass(bufferAddress, mTestHandler,
+                                            getCheckerCenter(), getCheckerRadius(),
+                                            mExposureLevel);
+            mCamera.startPreview();
+
+            synchronized (mProcessingImage) {
+                mProcessingImage.notifyAll();
+            }
+        }
+    };
+
+    @Override
+    public String getTestName(int index) {
+        switch (index) {
+            case 0:
+                return "EC All Range";
+            default:
+                return String.format("EC %d -> %d", (index - mNumTests / 2 - 1) * 10,
+                                     (index - mNumTests / 2) * 10);
+        }
+    }
+
+    @Override
+    public String getResult(int index) {
+        return mTestResults[index];
+    }
+
+    @Override
+    public int getNumTests() {
+        return mNumTests + 1;
+    }
+
+    @Override
+    public String getTestName() {
+        return "Exposure Compensation Test: \n";
+    }
+
+    private native long createExposureCompensationTest(int outputHeight, int outputWidth);
+
+    private native void createExposureCompensationClass(long bufferAddress, long handlerAddress,
+            long checkerCenterAddress, long checkerAadiusAddress, float mExposureLevel);
+
+    private native void processExposureCompensationTest(long handlerAddress);
+
+    static {
+        System.loadLibrary("cameraanalyzer");
+    }
+}