Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6caba0f5 authored by Christine Franks's avatar Christine Franks
Browse files

Split display white balance into a separate helper

Bug: 124474236
Test: atest FrameworksServicesTests:DisplayWhiteBalanceTintControllerTest

Change-Id: I0a9ac371a2ea5af6bcbfa86fd264387bba281ffb
parent c4ef6428
Loading
Loading
Loading
Loading
+1 −244
Original line number Diff line number Diff line
@@ -24,7 +24,6 @@ import static android.hardware.display.ColorDisplayManager.COLOR_MODE_BOOSTED;
import static android.hardware.display.ColorDisplayManager.COLOR_MODE_NATURAL;
import static android.hardware.display.ColorDisplayManager.COLOR_MODE_SATURATED;

import static com.android.server.display.color.DisplayTransformManager.LEVEL_COLOR_MATRIX_DISPLAY_WHITE_BALANCE;
import static com.android.server.display.color.DisplayTransformManager.LEVEL_COLOR_MATRIX_NIGHT_DISPLAY;

import android.Manifest;
@@ -45,7 +44,6 @@ import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.graphics.ColorSpace;
import android.hardware.display.ColorDisplayManager;
import android.hardware.display.ColorDisplayManager.AutoMode;
import android.hardware.display.ColorDisplayManager.ColorMode;
@@ -55,7 +53,6 @@ import android.net.Uri;
import android.opengl.Matrix;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.SystemProperties;
@@ -65,7 +62,6 @@ import android.provider.Settings.System;
import android.util.MathUtils;
import android.util.Slog;
import android.view.SurfaceControl;
import android.view.SurfaceControl.DisplayPrimaries;
import android.view.accessibility.AccessibilityManager;
import android.view.animation.AnimationUtils;

@@ -98,7 +94,7 @@ public final class ColorDisplayService extends SystemService {
    /**
     * The identity matrix, used if one of the given matrices is {@code null}.
     */
    private static final float[] MATRIX_IDENTITY = new float[16];
    static final float[] MATRIX_IDENTITY = new float[16];

    static {
        Matrix.setIdentityM(MATRIX_IDENTITY, 0);
@@ -1187,245 +1183,6 @@ public final class ColorDisplayService extends SystemService {
        }
    }

    final class DisplayWhiteBalanceTintController extends TintController {

        // Three chromaticity coordinates per color: X, Y, and Z
        private static final int NUM_VALUES_PER_PRIMARY = 3;
        // Four colors: red, green, blue, and white
        private static final int NUM_DISPLAY_PRIMARIES_VALS = 4 * NUM_VALUES_PER_PRIMARY;

        private final Object mLock = new Object();
        @VisibleForTesting
        int mTemperatureMin;
        @VisibleForTesting
        int mTemperatureMax;
        private int mTemperatureDefault;
        private float[] mDisplayNominalWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY];
        @VisibleForTesting
        ColorSpace.Rgb mDisplayColorSpaceRGB;
        private float[] mChromaticAdaptationMatrix;
        @VisibleForTesting
        int mCurrentColorTemperature;
        private float[] mCurrentColorTemperatureXYZ;
        private boolean mSetUp = false;
        private float[] mMatrixDisplayWhiteBalance = new float[16];
        private Boolean mIsAvailable;

        @Override
        public void setUp(Context context, boolean needsLinear) {
            mSetUp = false;
            final Resources res = context.getResources();

            ColorSpace.Rgb displayColorSpaceRGB = getDisplayColorSpaceFromSurfaceControl();
            if (displayColorSpaceRGB == null) {
                Slog.w(TAG, "Failed to get display color space from SurfaceControl, trying res");
                displayColorSpaceRGB = getDisplayColorSpaceFromResources(res);
                if (displayColorSpaceRGB == null) {
                    Slog.e(TAG, "Failed to get display color space from resources");
                    return;
                }
            }

            final String[] nominalWhiteValues = res.getStringArray(
                    R.array.config_displayWhiteBalanceDisplayNominalWhite);
            float[] displayNominalWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY];
            for (int i = 0; i < nominalWhiteValues.length; i++) {
                displayNominalWhiteXYZ[i] = Float.parseFloat(nominalWhiteValues[i]);
            }

            final int colorTemperatureMin = res.getInteger(
                    R.integer.config_displayWhiteBalanceColorTemperatureMin);
            if (colorTemperatureMin <= 0) {
                Slog.e(TAG, "Display white balance minimum temperature must be greater than 0");
                return;
            }

            final int colorTemperatureMax = res.getInteger(
                    R.integer.config_displayWhiteBalanceColorTemperatureMax);
            if (colorTemperatureMax < colorTemperatureMin) {
                Slog.e(TAG, "Display white balance max temp must be greater or equal to min");
                return;
            }

            final int colorTemperature = res.getInteger(
                    R.integer.config_displayWhiteBalanceColorTemperatureDefault);

            synchronized (mLock) {
                mDisplayColorSpaceRGB = displayColorSpaceRGB;
                mDisplayNominalWhiteXYZ = displayNominalWhiteXYZ;
                mTemperatureMin = colorTemperatureMin;
                mTemperatureMax = colorTemperatureMax;
                mTemperatureDefault = colorTemperature;
                mSetUp = true;
            }

            setMatrix(mTemperatureDefault);
        }

        @Override
        public float[] getMatrix() {
            return mSetUp && isActivated() ? mMatrixDisplayWhiteBalance : MATRIX_IDENTITY;
        }

        @Override
        public void setMatrix(int cct) {
            if (!mSetUp) {
                Slog.w(TAG, "Can't set display white balance temperature: uninitialized");
                return;
            }

            if (cct < mTemperatureMin) {
                Slog.w(TAG, "Requested display color temperature is below allowed minimum");
                cct = mTemperatureMin;
            } else if (cct > mTemperatureMax) {
                Slog.w(TAG, "Requested display color temperature is above allowed maximum");
                cct = mTemperatureMax;
            }

            Slog.d(TAG, "setDisplayWhiteBalanceTemperatureMatrix: cct = " + cct);

            synchronized (mLock) {
                mCurrentColorTemperature = cct;

                // Adapt the display's nominal white point to match the requested CCT value
                mCurrentColorTemperatureXYZ = ColorSpace.cctToXyz(cct);

                mChromaticAdaptationMatrix =
                        ColorSpace.chromaticAdaptation(ColorSpace.Adaptation.BRADFORD,
                                mDisplayNominalWhiteXYZ, mCurrentColorTemperatureXYZ);

                // Convert the adaptation matrix to RGB space
                float[] result = ColorSpace.mul3x3(mChromaticAdaptationMatrix,
                        mDisplayColorSpaceRGB.getTransform());
                result = ColorSpace.mul3x3(mDisplayColorSpaceRGB.getInverseTransform(), result);

                // Normalize the transform matrix to peak white value in RGB space
                final float adaptedMaxR = result[0] + result[3] + result[6];
                final float adaptedMaxG = result[1] + result[4] + result[7];
                final float adaptedMaxB = result[2] + result[5] + result[8];
                final float denum = Math.max(Math.max(adaptedMaxR, adaptedMaxG), adaptedMaxB);
                for (int i = 0; i < result.length; i++) {
                    result[i] /= denum;
                }

                Matrix.setIdentityM(mMatrixDisplayWhiteBalance, 0);
                java.lang.System.arraycopy(result, 0, mMatrixDisplayWhiteBalance, 0, 3);
                java.lang.System.arraycopy(result, 3, mMatrixDisplayWhiteBalance, 4, 3);
                java.lang.System.arraycopy(result, 6, mMatrixDisplayWhiteBalance, 8, 3);
            }
        }

        @Override
        public int getLevel() {
            return LEVEL_COLOR_MATRIX_DISPLAY_WHITE_BALANCE;
        }

        @Override
        public boolean isAvailable(Context context) {
            if (mIsAvailable == null) {
                mIsAvailable = ColorDisplayManager.isDisplayWhiteBalanceAvailable(context);
            }
            return mIsAvailable;
        }

        /**
         * Format a given matrix into a string.
         *
         * @param matrix the matrix to format
         * @param cols number of columns in the matrix
         */
        private String matrixToString(float[] matrix, int cols) {
            if (matrix == null || cols <= 0) {
                Slog.e(TAG, "Invalid arguments when formatting matrix to string");
                return "";
            }

            StringBuilder sb = new StringBuilder("");
            for (int i = 0; i < matrix.length; i++) {
                if (i % cols == 0) {
                    sb.append("\n      ");
                }
                sb.append(String.format("%9.6f ", matrix[i]));
            }
            return sb.toString();
        }

        @Override
        public void dump(PrintWriter pw) {
            synchronized (mLock) {
                pw.println("    mSetUp = " + mSetUp);
                if (!mSetUp) {
                    return;
                }

                pw.println("    mTemperatureMin = " + mTemperatureMin);
                pw.println("    mTemperatureMax = " + mTemperatureMax);
                pw.println("    mTemperatureDefault = " + mTemperatureDefault);
                pw.println("    mCurrentColorTemperature = " + mCurrentColorTemperature);
                pw.println("    mCurrentColorTemperatureXYZ = "
                        + matrixToString(mCurrentColorTemperatureXYZ, 3));
                pw.println("    mDisplayColorSpaceRGB RGB-to-XYZ = "
                        + matrixToString(mDisplayColorSpaceRGB.getTransform(), 3));
                pw.println("    mChromaticAdaptationMatrix = "
                        + matrixToString(mChromaticAdaptationMatrix, 3));
                pw.println("    mDisplayColorSpaceRGB XYZ-to-RGB = "
                        + matrixToString(mDisplayColorSpaceRGB.getInverseTransform(), 3));
                pw.println("    mMatrixDisplayWhiteBalance = "
                        + matrixToString(mMatrixDisplayWhiteBalance, 4));
            }
        }

        private ColorSpace.Rgb makeRgbColorSpaceFromXYZ(float[] redGreenBlueXYZ, float[] whiteXYZ) {
            return new ColorSpace.Rgb(
                    "Display Color Space",
                    redGreenBlueXYZ,
                    whiteXYZ,
                    2.2f // gamma, unused for display white balance
            );
        }

        private ColorSpace.Rgb getDisplayColorSpaceFromSurfaceControl() {
            final IBinder displayToken = SurfaceControl.getInternalDisplayToken();
            if (displayToken == null) {
                return null;
            }

            DisplayPrimaries primaries = SurfaceControl.getDisplayNativePrimaries(displayToken);
            if (primaries == null || primaries.red == null || primaries.green == null
                    || primaries.blue == null || primaries.white == null) {
                return null;
            }

            return makeRgbColorSpaceFromXYZ(
                    new float[]{
                            primaries.red.X, primaries.red.Y, primaries.red.Z,
                            primaries.green.X, primaries.green.Y, primaries.green.Z,
                            primaries.blue.X, primaries.blue.Y, primaries.blue.Z,
                    },
                    new float[]{primaries.white.X, primaries.white.Y, primaries.white.Z}
            );
        }

        private ColorSpace.Rgb getDisplayColorSpaceFromResources(Resources res) {
            final String[] displayPrimariesValues = res.getStringArray(
                    R.array.config_displayWhiteBalanceDisplayPrimaries);
            float[] displayRedGreenBlueXYZ =
                    new float[NUM_DISPLAY_PRIMARIES_VALS - NUM_VALUES_PER_PRIMARY];
            float[] displayWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY];

            for (int i = 0; i < displayRedGreenBlueXYZ.length; i++) {
                displayRedGreenBlueXYZ[i] = Float.parseFloat(displayPrimariesValues[i]);
            }

            for (int i = 0; i < displayWhiteXYZ.length; i++) {
                displayWhiteXYZ[i] = Float.parseFloat(
                        displayPrimariesValues[displayRedGreenBlueXYZ.length + i]);
            }

            return makeRgbColorSpaceFromXYZ(displayRedGreenBlueXYZ, displayWhiteXYZ);
        }
    }

    /**
     * Local service that allows color transforms to be enabled from other system services.
     */
+280 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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.server.display.color;

import static com.android.server.display.color.DisplayTransformManager.LEVEL_COLOR_MATRIX_DISPLAY_WHITE_BALANCE;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.ColorSpace;
import android.hardware.display.ColorDisplayManager;
import android.opengl.Matrix;
import android.os.IBinder;
import android.util.Slog;
import android.view.SurfaceControl;
import android.view.SurfaceControl.DisplayPrimaries;

import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;

import java.io.PrintWriter;

final class DisplayWhiteBalanceTintController extends TintController {

    // Three chromaticity coordinates per color: X, Y, and Z
    private static final int NUM_VALUES_PER_PRIMARY = 3;
    // Four colors: red, green, blue, and white
    private static final int NUM_DISPLAY_PRIMARIES_VALS = 4 * NUM_VALUES_PER_PRIMARY;

    private final Object mLock = new Object();
    @VisibleForTesting
    int mTemperatureMin;
    @VisibleForTesting
    int mTemperatureMax;
    private int mTemperatureDefault;
    private float[] mDisplayNominalWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY];
    @VisibleForTesting
    ColorSpace.Rgb mDisplayColorSpaceRGB;
    private float[] mChromaticAdaptationMatrix;
    @VisibleForTesting
    int mCurrentColorTemperature;
    private float[] mCurrentColorTemperatureXYZ;
    private boolean mSetUp = false;
    private float[] mMatrixDisplayWhiteBalance = new float[16];
    private Boolean mIsAvailable;

    @Override
    public void setUp(Context context, boolean needsLinear) {
        mSetUp = false;
        final Resources res = context.getResources();

        ColorSpace.Rgb displayColorSpaceRGB = getDisplayColorSpaceFromSurfaceControl();
        if (displayColorSpaceRGB == null) {
            Slog.w(ColorDisplayService.TAG,
                    "Failed to get display color space from SurfaceControl, trying res");
            displayColorSpaceRGB = getDisplayColorSpaceFromResources(res);
            if (displayColorSpaceRGB == null) {
                Slog.e(ColorDisplayService.TAG, "Failed to get display color space from resources");
                return;
            }
        }

        final String[] nominalWhiteValues = res.getStringArray(
                R.array.config_displayWhiteBalanceDisplayNominalWhite);
        float[] displayNominalWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY];
        for (int i = 0; i < nominalWhiteValues.length; i++) {
            displayNominalWhiteXYZ[i] = Float.parseFloat(nominalWhiteValues[i]);
        }

        final int colorTemperatureMin = res.getInteger(
                R.integer.config_displayWhiteBalanceColorTemperatureMin);
        if (colorTemperatureMin <= 0) {
            Slog.e(ColorDisplayService.TAG,
                    "Display white balance minimum temperature must be greater than 0");
            return;
        }

        final int colorTemperatureMax = res.getInteger(
                R.integer.config_displayWhiteBalanceColorTemperatureMax);
        if (colorTemperatureMax < colorTemperatureMin) {
            Slog.e(ColorDisplayService.TAG,
                    "Display white balance max temp must be greater or equal to min");
            return;
        }

        final int colorTemperature = res.getInteger(
                R.integer.config_displayWhiteBalanceColorTemperatureDefault);

        synchronized (mLock) {
            mDisplayColorSpaceRGB = displayColorSpaceRGB;
            mDisplayNominalWhiteXYZ = displayNominalWhiteXYZ;
            mTemperatureMin = colorTemperatureMin;
            mTemperatureMax = colorTemperatureMax;
            mTemperatureDefault = colorTemperature;
            mSetUp = true;
        }

        setMatrix(mTemperatureDefault);
    }

    @Override
    public float[] getMatrix() {
        return mSetUp && isActivated() ? mMatrixDisplayWhiteBalance
                : ColorDisplayService.MATRIX_IDENTITY;
    }

    @Override
    public void setMatrix(int cct) {
        if (!mSetUp) {
            Slog.w(ColorDisplayService.TAG,
                    "Can't set display white balance temperature: uninitialized");
            return;
        }

        if (cct < mTemperatureMin) {
            Slog.w(ColorDisplayService.TAG,
                    "Requested display color temperature is below allowed minimum");
            cct = mTemperatureMin;
        } else if (cct > mTemperatureMax) {
            Slog.w(ColorDisplayService.TAG,
                    "Requested display color temperature is above allowed maximum");
            cct = mTemperatureMax;
        }

        Slog.d(ColorDisplayService.TAG, "setDisplayWhiteBalanceTemperatureMatrix: cct = " + cct);

        synchronized (mLock) {
            mCurrentColorTemperature = cct;

            // Adapt the display's nominal white point to match the requested CCT value
            mCurrentColorTemperatureXYZ = ColorSpace.cctToXyz(cct);

            mChromaticAdaptationMatrix =
                    ColorSpace.chromaticAdaptation(ColorSpace.Adaptation.BRADFORD,
                            mDisplayNominalWhiteXYZ, mCurrentColorTemperatureXYZ);

            // Convert the adaptation matrix to RGB space
            float[] result = ColorSpace.mul3x3(mChromaticAdaptationMatrix,
                    mDisplayColorSpaceRGB.getTransform());
            result = ColorSpace.mul3x3(mDisplayColorSpaceRGB.getInverseTransform(), result);

            // Normalize the transform matrix to peak white value in RGB space
            final float adaptedMaxR = result[0] + result[3] + result[6];
            final float adaptedMaxG = result[1] + result[4] + result[7];
            final float adaptedMaxB = result[2] + result[5] + result[8];
            final float denum = Math.max(Math.max(adaptedMaxR, adaptedMaxG), adaptedMaxB);
            for (int i = 0; i < result.length; i++) {
                result[i] /= denum;
            }

            Matrix.setIdentityM(mMatrixDisplayWhiteBalance, 0);
            java.lang.System.arraycopy(result, 0, mMatrixDisplayWhiteBalance, 0, 3);
            java.lang.System.arraycopy(result, 3, mMatrixDisplayWhiteBalance, 4, 3);
            java.lang.System.arraycopy(result, 6, mMatrixDisplayWhiteBalance, 8, 3);
        }
    }

    @Override
    public int getLevel() {
        return LEVEL_COLOR_MATRIX_DISPLAY_WHITE_BALANCE;
    }

    @Override
    public boolean isAvailable(Context context) {
        if (mIsAvailable == null) {
            mIsAvailable = ColorDisplayManager.isDisplayWhiteBalanceAvailable(context);
        }
        return mIsAvailable;
    }

    @Override
    public void dump(PrintWriter pw) {
        synchronized (mLock) {
            pw.println("    mSetUp = " + mSetUp);
            if (!mSetUp) {
                return;
            }

            pw.println("    mTemperatureMin = " + mTemperatureMin);
            pw.println("    mTemperatureMax = " + mTemperatureMax);
            pw.println("    mTemperatureDefault = " + mTemperatureDefault);
            pw.println("    mCurrentColorTemperature = " + mCurrentColorTemperature);
            pw.println("    mCurrentColorTemperatureXYZ = "
                    + matrixToString(mCurrentColorTemperatureXYZ, 3));
            pw.println("    mDisplayColorSpaceRGB RGB-to-XYZ = "
                    + matrixToString(mDisplayColorSpaceRGB.getTransform(), 3));
            pw.println("    mChromaticAdaptationMatrix = "
                    + matrixToString(mChromaticAdaptationMatrix, 3));
            pw.println("    mDisplayColorSpaceRGB XYZ-to-RGB = "
                    + matrixToString(mDisplayColorSpaceRGB.getInverseTransform(), 3));
            pw.println("    mMatrixDisplayWhiteBalance = "
                    + matrixToString(mMatrixDisplayWhiteBalance, 4));
        }
    }

    /**
     * Format a given matrix into a string.
     *
     * @param matrix the matrix to format
     * @param columns number of columns in the matrix
     */
    private String matrixToString(float[] matrix, int columns) {
        if (matrix == null || columns <= 0) {
            Slog.e(ColorDisplayService.TAG, "Invalid arguments when formatting matrix to string");
            return "";
        }

        final StringBuilder sb = new StringBuilder("");
        for (int i = 0; i < matrix.length; i++) {
            if (i % columns == 0) {
                sb.append("\n      ");
            }
            sb.append(String.format("%9.6f", matrix[i]));
        }
        return sb.toString();
    }

    private ColorSpace.Rgb makeRgbColorSpaceFromXYZ(float[] redGreenBlueXYZ, float[] whiteXYZ) {
        return new ColorSpace.Rgb(
                "Display Color Space",
                redGreenBlueXYZ,
                whiteXYZ,
                2.2f // gamma, unused for display white balance
        );
    }

    private ColorSpace.Rgb getDisplayColorSpaceFromSurfaceControl() {
        final IBinder displayToken = SurfaceControl.getInternalDisplayToken();
        if (displayToken == null) {
            return null;
        }

        DisplayPrimaries primaries = SurfaceControl.getDisplayNativePrimaries(displayToken);
        if (primaries == null || primaries.red == null || primaries.green == null
                || primaries.blue == null || primaries.white == null) {
            return null;
        }

        return makeRgbColorSpaceFromXYZ(
                new float[]{
                        primaries.red.X, primaries.red.Y, primaries.red.Z,
                        primaries.green.X, primaries.green.Y, primaries.green.Z,
                        primaries.blue.X, primaries.blue.Y, primaries.blue.Z,
                },
                new float[]{primaries.white.X, primaries.white.Y, primaries.white.Z}
        );
    }

    private ColorSpace.Rgb getDisplayColorSpaceFromResources(Resources res) {
        final String[] displayPrimariesValues = res.getStringArray(
                R.array.config_displayWhiteBalanceDisplayPrimaries);
        float[] displayRedGreenBlueXYZ =
                new float[NUM_DISPLAY_PRIMARIES_VALS - NUM_VALUES_PER_PRIMARY];
        float[] displayWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY];

        for (int i = 0; i < displayRedGreenBlueXYZ.length; i++) {
            displayRedGreenBlueXYZ[i] = Float.parseFloat(displayPrimariesValues[i]);
        }

        for (int i = 0; i < displayWhiteXYZ.length; i++) {
            displayWhiteXYZ[i] = Float.parseFloat(
                    displayPrimariesValues[displayRedGreenBlueXYZ.length + i]);
        }

        return makeRgbColorSpaceFromXYZ(displayRedGreenBlueXYZ, displayWhiteXYZ);
    }
}
+1 −1
Original line number Diff line number Diff line
@@ -26,7 +26,7 @@ import android.util.Slog;
import java.util.Arrays;

/** Control the color transform for global device saturation. */
public class GlobalSaturationTintController extends TintController {
final class GlobalSaturationTintController extends TintController {

    private final float[] mMatrixGlobalSaturation = new float[16];

+89 −136

File changed.

Preview size limit exceeded, changes collapsed.

+62 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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.server.display.color;

import static com.google.common.truth.Truth.assertWithMessage;

import org.junit.Before;
import org.junit.Test;

public class DisplayWhiteBalanceTintControllerTest {

    private DisplayWhiteBalanceTintController mDisplayWhiteBalanceTintController;

    @Before
    public void setUp() {
        mDisplayWhiteBalanceTintController = new DisplayWhiteBalanceTintController();
    }

    @Test
    public void displayWhiteBalance_setTemperatureOverMax() {
        final int max = mDisplayWhiteBalanceTintController.mTemperatureMax;
        mDisplayWhiteBalanceTintController.setMatrix(max + 1);
        assertWithMessage("Unexpected temperature set")
                .that(mDisplayWhiteBalanceTintController.mCurrentColorTemperature)
                .isEqualTo(max);
    }

    @Test
    public void displayWhiteBalance_setTemperatureBelowMin() {
        final int min = mDisplayWhiteBalanceTintController.mTemperatureMin;
        mDisplayWhiteBalanceTintController.setMatrix(min - 1);
        assertWithMessage("Unexpected temperature set")
                .that(mDisplayWhiteBalanceTintController.mCurrentColorTemperature)
                .isEqualTo(min);
    }

    @Test
    public void displayWhiteBalance_setValidTemperature() {
        final int colorTemperature = (mDisplayWhiteBalanceTintController.mTemperatureMin
                + mDisplayWhiteBalanceTintController.mTemperatureMax) / 2;
        mDisplayWhiteBalanceTintController.setMatrix(colorTemperature);

        assertWithMessage("Unexpected temperature set")
                .that(mDisplayWhiteBalanceTintController.mCurrentColorTemperature)
                .isEqualTo(colorTemperature);
    }

}