1f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette/*
2f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette * Copyright (C) 2013 The Android Open Source Project
3f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette *
4f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette * Licensed under the Apache License, Version 2.0 (the "License");
5f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette * you may not use this file except in compliance with the License.
6f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette * You may obtain a copy of the License at
7f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette *
8f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette *      http://www.apache.org/licenses/LICENSE-2.0
9f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette *
10f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette * Unless required by applicable law or agreed to in writing, software
11f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette * distributed under the License is distributed on an "AS IS" BASIS,
12f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette * See the License for the specific language governing permissions and
14f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette * limitations under the License.
15f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette */
16f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette
17f242659afc6539f9f1104833a88e37b5f9203417Alan Viverettepackage com.android.server.accessibility;
18f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette
19f242659afc6539f9f1104833a88e37b5f9203417Alan Viveretteimport android.content.ContentResolver;
20f242659afc6539f9f1104833a88e37b5f9203417Alan Viveretteimport android.content.Context;
21f242659afc6539f9f1104833a88e37b5f9203417Alan Viveretteimport android.opengl.Matrix;
22f242659afc6539f9f1104833a88e37b5f9203417Alan Viveretteimport android.os.IBinder;
23f242659afc6539f9f1104833a88e37b5f9203417Alan Viveretteimport android.os.Parcel;
24f242659afc6539f9f1104833a88e37b5f9203417Alan Viveretteimport android.os.RemoteException;
25f242659afc6539f9f1104833a88e37b5f9203417Alan Viveretteimport android.os.ServiceManager;
26f242659afc6539f9f1104833a88e37b5f9203417Alan Viveretteimport android.provider.Settings;
27f242659afc6539f9f1104833a88e37b5f9203417Alan Viveretteimport android.util.Slog;
28f242659afc6539f9f1104833a88e37b5f9203417Alan Viveretteimport android.view.accessibility.AccessibilityManager;
29f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette
30f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette/**
31f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette * Utility methods for performing accessibility display adjustments.
32f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette */
33f242659afc6539f9f1104833a88e37b5f9203417Alan Viveretteclass DisplayAdjustmentUtils {
34f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette    private static final String LOG_TAG = DisplayAdjustmentUtils.class.getSimpleName();
35f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette
36f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette    /** Matrix and offset used for converting color to gray-scale. */
37f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette    private static final float[] GRAYSCALE_MATRIX = new float[] {
38f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette        .2126f, .2126f, .2126f, 0,
39f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette        .7152f, .7152f, .7152f, 0,
40f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette        .0722f, .0722f, .0722f, 0,
41f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette             0,      0,      0, 1
42f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette    };
43f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette
446437518061fc8718590e0272ed17ea64710d2299Alan Viverette    /**
456437518061fc8718590e0272ed17ea64710d2299Alan Viverette     * Matrix and offset used for luminance inversion. Represents a transform
466437518061fc8718590e0272ed17ea64710d2299Alan Viverette     * from RGB to YIQ color space, rotation around the Y axis by 180 degrees,
476437518061fc8718590e0272ed17ea64710d2299Alan Viverette     * transform back to RGB color space, and subtraction from 1. The last row
486437518061fc8718590e0272ed17ea64710d2299Alan Viverette     * represents a non-multiplied addition, see surfaceflinger's ProgramCache
496437518061fc8718590e0272ed17ea64710d2299Alan Viverette     * for full implementation details.
506437518061fc8718590e0272ed17ea64710d2299Alan Viverette     */
51f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette    private static final float[] INVERSION_MATRIX_VALUE_ONLY = new float[] {
526437518061fc8718590e0272ed17ea64710d2299Alan Viverette        0.402f, -0.598f, -0.599f, 0,
536437518061fc8718590e0272ed17ea64710d2299Alan Viverette       -1.174f, -0.174f, -1.175f, 0,
546437518061fc8718590e0272ed17ea64710d2299Alan Viverette       -0.228f, -0.228f,  0.772f, 0,
556437518061fc8718590e0272ed17ea64710d2299Alan Viverette             1,       1,       1, 1
56f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette    };
57f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette
58f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette    /** Default inversion mode for display color correction. */
59f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette    private static final int DEFAULT_DISPLAY_DALTONIZER =
60f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette            AccessibilityManager.DALTONIZER_CORRECT_DEUTERANOMALY;
61f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette
62f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette    /**
63f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette     * Returns whether the specified user with has any display color
64f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette     * adjustments.
65f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette     */
66f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette    public static boolean hasAdjustments(Context context, int userId) {
67f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette        final ContentResolver cr = context.getContentResolver();
68f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette
698864415e2a29daf504b52afe911eb5c5a8b03fc0Jeff Brown        if (Settings.Secure.getIntForUser(cr,
708864415e2a29daf504b52afe911eb5c5a8b03fc0Jeff Brown                Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, 0, userId) != 0) {
718864415e2a29daf504b52afe911eb5c5a8b03fc0Jeff Brown            return true;
728864415e2a29daf504b52afe911eb5c5a8b03fc0Jeff Brown        }
73f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette
748864415e2a29daf504b52afe911eb5c5a8b03fc0Jeff Brown        if (Settings.Secure.getIntForUser(cr,
758864415e2a29daf504b52afe911eb5c5a8b03fc0Jeff Brown                Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, 0, userId) != 0) {
768864415e2a29daf504b52afe911eb5c5a8b03fc0Jeff Brown            return true;
77f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette        }
78f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette
798864415e2a29daf504b52afe911eb5c5a8b03fc0Jeff Brown        return false;
80f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette    }
81f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette
82f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette    /**
83f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette     * Applies the specified user's display color adjustments.
84f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette     */
85f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette    public static void applyAdjustments(Context context, int userId) {
86f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette        final ContentResolver cr = context.getContentResolver();
878864415e2a29daf504b52afe911eb5c5a8b03fc0Jeff Brown        float[] colorMatrix = null;
88f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette
898864415e2a29daf504b52afe911eb5c5a8b03fc0Jeff Brown        if (Settings.Secure.getIntForUser(cr,
908864415e2a29daf504b52afe911eb5c5a8b03fc0Jeff Brown                Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, 0, userId) != 0) {
918864415e2a29daf504b52afe911eb5c5a8b03fc0Jeff Brown            colorMatrix = multiply(colorMatrix, INVERSION_MATRIX_VALUE_ONLY);
92f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette        }
93f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette
948864415e2a29daf504b52afe911eb5c5a8b03fc0Jeff Brown        if (Settings.Secure.getIntForUser(cr,
958864415e2a29daf504b52afe911eb5c5a8b03fc0Jeff Brown                Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, 0, userId) != 0) {
96f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette            final int daltonizerMode = Settings.Secure.getIntForUser(cr,
97f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette                    Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER, DEFAULT_DISPLAY_DALTONIZER,
98f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette                    userId);
99f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette            // Monochromacy isn't supported by the native Daltonizer.
100f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette            if (daltonizerMode == AccessibilityManager.DALTONIZER_SIMULATE_MONOCHROMACY) {
1018864415e2a29daf504b52afe911eb5c5a8b03fc0Jeff Brown                colorMatrix = multiply(colorMatrix, GRAYSCALE_MATRIX);
1028864415e2a29daf504b52afe911eb5c5a8b03fc0Jeff Brown                setDaltonizerMode(AccessibilityManager.DALTONIZER_DISABLED);
103f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette            } else {
1048864415e2a29daf504b52afe911eb5c5a8b03fc0Jeff Brown                setDaltonizerMode(daltonizerMode);
105f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette            }
106f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette        } else {
1078864415e2a29daf504b52afe911eb5c5a8b03fc0Jeff Brown            setDaltonizerMode(AccessibilityManager.DALTONIZER_DISABLED);
108f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette        }
109f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette
1108864415e2a29daf504b52afe911eb5c5a8b03fc0Jeff Brown        setColorTransform(colorMatrix);
1118864415e2a29daf504b52afe911eb5c5a8b03fc0Jeff Brown    }
1128864415e2a29daf504b52afe911eb5c5a8b03fc0Jeff Brown
1138864415e2a29daf504b52afe911eb5c5a8b03fc0Jeff Brown    private static float[] multiply(float[] matrix, float[] other) {
1148864415e2a29daf504b52afe911eb5c5a8b03fc0Jeff Brown        if (matrix == null) {
1158864415e2a29daf504b52afe911eb5c5a8b03fc0Jeff Brown            return other;
116f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette        }
1178864415e2a29daf504b52afe911eb5c5a8b03fc0Jeff Brown        float[] result = new float[16];
1188864415e2a29daf504b52afe911eb5c5a8b03fc0Jeff Brown        Matrix.multiplyMM(result, 0, matrix, 0, other, 0);
1198864415e2a29daf504b52afe911eb5c5a8b03fc0Jeff Brown        return result;
120f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette    }
121f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette
122f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette    /**
123f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette     * Sets the surface flinger's Daltonization mode. This adjusts the color
124f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette     * space to correct for or simulate various types of color blindness.
125f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette     *
126f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette     * @param mode new Daltonization mode
127f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette     */
1288864415e2a29daf504b52afe911eb5c5a8b03fc0Jeff Brown    private static void setDaltonizerMode(int mode) {
129f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette        try {
130f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette            final IBinder flinger = ServiceManager.getService("SurfaceFlinger");
131f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette            if (flinger != null) {
132f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette                final Parcel data = Parcel.obtain();
133f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette                data.writeInterfaceToken("android.ui.ISurfaceComposer");
134f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette                data.writeInt(mode);
135f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette                flinger.transact(1014, data, null, 0);
136f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette                data.recycle();
137f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette            }
138f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette        } catch (RemoteException ex) {
139f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette            Slog.e(LOG_TAG, "Failed to set Daltonizer mode", ex);
140f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette        }
141f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette    }
142f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette
143f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette    /**
144f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette     * Sets the surface flinger's color transformation as a 4x4 matrix. If the
145f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette     * matrix is null, color transformations are disabled.
146f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette     *
147f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette     * @param m the float array that holds the transformation matrix, or null to
148f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette     *            disable transformation
149f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette     */
1508864415e2a29daf504b52afe911eb5c5a8b03fc0Jeff Brown    private static void setColorTransform(float[] m) {
151f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette        try {
152f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette            final IBinder flinger = ServiceManager.getService("SurfaceFlinger");
153f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette            if (flinger != null) {
154f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette                final Parcel data = Parcel.obtain();
155f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette                data.writeInterfaceToken("android.ui.ISurfaceComposer");
156f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette                if (m != null) {
157f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette                    data.writeInt(1);
158f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette                    for (int i = 0; i < 16; i++) {
159f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette                        data.writeFloat(m[i]);
160f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette                    }
161f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette                } else {
162f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette                    data.writeInt(0);
163f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette                }
164f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette                flinger.transact(1015, data, null, 0);
165f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette                data.recycle();
166f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette            }
167f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette        } catch (RemoteException ex) {
168f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette            Slog.e(LOG_TAG, "Failed to set color transform", ex);
169f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette        }
170f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette    }
171f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette
172f242659afc6539f9f1104833a88e37b5f9203417Alan Viverette}
173