DisplayAdjustmentUtils.java revision 19c662b3df3b35756a92282bb6cc767e6407cb8a
1/* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.server.accessibility; 18 19import android.content.ContentResolver; 20import android.content.Context; 21import android.opengl.Matrix; 22import android.os.IBinder; 23import android.os.Parcel; 24import android.os.RemoteException; 25import android.os.ServiceManager; 26import android.provider.Settings; 27import android.util.Slog; 28import android.view.accessibility.AccessibilityManager; 29 30/** 31 * Utility methods for performing accessibility display adjustments. 32 */ 33class DisplayAdjustmentUtils { 34 private static final String LOG_TAG = DisplayAdjustmentUtils.class.getSimpleName(); 35 36 /** Matrix and offset used for converting color to gray-scale. */ 37 private static final float[] GRAYSCALE_MATRIX = new float[] { 38 .2126f, .2126f, .2126f, 0, 39 .7152f, .7152f, .7152f, 0, 40 .0722f, .0722f, .0722f, 0, 41 0, 0, 0, 1 42 }; 43 44 /** Matrix and offset used for standard display inversion. */ 45 private static final float[] INVERSION_MATRIX_STANDARD = new float[] { 46 -1, 0, 0, 0, 47 0, -1, 0, 0, 48 0, 0, -1, 0, 49 1, 1, 1, 1 50 }; 51 52 /** Matrix and offset used for hue-only display inversion. */ 53 private static final float[] INVERSION_MATRIX_HUE_ONLY = new float[] { 54 0, .5f, .5f, 0, 55 .5f, 0, .5f, 0, 56 .5f, .5f, 0, 0, 57 0, 0, 0, 1 58 }; 59 60 /** Matrix and offset used for value-only display inversion. */ 61 private static final float[] INVERSION_MATRIX_VALUE_ONLY = new float[] { 62 0, -.5f, -.5f, 0, 63 -.5f, 0, -.5f, 0, 64 -.5f, -.5f, 0, 0, 65 1, 1, 1, 1 66 }; 67 68 /** Default contrast for display contrast enhancement. */ 69 private static final float DEFAULT_DISPLAY_CONTRAST = 2; 70 71 /** Default brightness for display contrast enhancement. */ 72 private static final float DEFAULT_DISPLAY_BRIGHTNESS = 0; 73 74 /** Default inversion mode for display color inversion. */ 75 private static final int DEFAULT_DISPLAY_INVERSION = AccessibilityManager.INVERSION_STANDARD; 76 77 /** Default inversion mode for display color correction. */ 78 private static final int DEFAULT_DISPLAY_DALTONIZER = 79 AccessibilityManager.DALTONIZER_CORRECT_DEUTERANOMALY; 80 81 /** 82 * Returns whether the specified user with has any display color 83 * adjustments. 84 */ 85 public static boolean hasAdjustments(Context context, int userId) { 86 final ContentResolver cr = context.getContentResolver(); 87 88 boolean hasColorTransform = Settings.Secure.getIntForUser( 89 cr, Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, 0, userId) == 1; 90 91 if (!hasColorTransform) { 92 hasColorTransform |= Settings.Secure.getIntForUser( 93 cr, Settings.Secure.ACCESSIBILITY_DISPLAY_CONTRAST_ENABLED, 0, userId) == 1; 94 } 95 96 if (!hasColorTransform) { 97 hasColorTransform |= Settings.Secure.getIntForUser( 98 cr, Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, 0, userId) == 1; 99 } 100 101 return hasColorTransform; 102 } 103 104 /** 105 * Applies the specified user's display color adjustments. 106 */ 107 public static void applyAdjustments(Context context, int userId) { 108 final ContentResolver cr = context.getContentResolver(); 109 float[] colorMatrix = new float[16]; 110 float[] outputMatrix = new float[16]; 111 boolean hasColorTransform = false; 112 113 Matrix.setIdentityM(colorMatrix, 0); 114 115 final boolean inversionEnabled = Settings.Secure.getIntForUser( 116 cr, Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, 0, userId) == 1; 117 if (inversionEnabled) { 118 final int inversionMode = Settings.Secure.getIntForUser(cr, 119 Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION, DEFAULT_DISPLAY_INVERSION, 120 userId); 121 final float[] inversionMatrix; 122 switch (inversionMode) { 123 case AccessibilityManager.INVERSION_HUE_ONLY: 124 inversionMatrix = INVERSION_MATRIX_HUE_ONLY; 125 break; 126 case AccessibilityManager.INVERSION_VALUE_ONLY: 127 inversionMatrix = INVERSION_MATRIX_VALUE_ONLY; 128 break; 129 default: 130 inversionMatrix = INVERSION_MATRIX_STANDARD; 131 } 132 133 Matrix.multiplyMM(outputMatrix, 0, colorMatrix, 0, inversionMatrix, 0); 134 135 colorMatrix = outputMatrix; 136 outputMatrix = colorMatrix; 137 138 hasColorTransform = true; 139 } 140 141 final boolean contrastEnabled = Settings.Secure.getIntForUser( 142 cr, Settings.Secure.ACCESSIBILITY_DISPLAY_CONTRAST_ENABLED, 0, userId) == 1; 143 if (contrastEnabled) { 144 final float contrast = Settings.Secure.getFloatForUser(cr, 145 Settings.Secure.ACCESSIBILITY_DISPLAY_CONTRAST, DEFAULT_DISPLAY_CONTRAST, 146 userId); 147 final float brightness = Settings.Secure.getFloatForUser(cr, 148 Settings.Secure.ACCESSIBILITY_DISPLAY_BRIGHTNESS, DEFAULT_DISPLAY_BRIGHTNESS, 149 userId); 150 final float off = brightness * contrast - 0.5f * contrast + 0.5f; 151 final float[] contrastMatrix = { 152 contrast, 0, 0, 0, 153 0, contrast, 0, 0, 154 0, 0, contrast, 0, 155 off, off, off, 1 156 }; 157 158 Matrix.multiplyMM(outputMatrix, 0, colorMatrix, 0, contrastMatrix, 0); 159 160 colorMatrix = outputMatrix; 161 outputMatrix = colorMatrix; 162 163 hasColorTransform = true; 164 } 165 166 final boolean daltonizerEnabled = Settings.Secure.getIntForUser( 167 cr, Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, 0, userId) != 0; 168 if (daltonizerEnabled) { 169 final int daltonizerMode = Settings.Secure.getIntForUser(cr, 170 Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER, DEFAULT_DISPLAY_DALTONIZER, 171 userId); 172 // Monochromacy isn't supported by the native Daltonizer. 173 if (daltonizerMode == AccessibilityManager.DALTONIZER_SIMULATE_MONOCHROMACY) { 174 Matrix.multiplyMM(outputMatrix, 0, colorMatrix, 0, GRAYSCALE_MATRIX, 0); 175 176 final float[] temp = colorMatrix; 177 colorMatrix = outputMatrix; 178 outputMatrix = temp; 179 180 hasColorTransform = true; 181 nativeSetDaltonizerMode(AccessibilityManager.DALTONIZER_DISABLED); 182 } else { 183 nativeSetDaltonizerMode(daltonizerMode); 184 } 185 } else { 186 nativeSetDaltonizerMode(AccessibilityManager.DALTONIZER_DISABLED); 187 } 188 189 if (hasColorTransform) { 190 nativeSetColorTransform(colorMatrix); 191 } else { 192 nativeSetColorTransform(null); 193 } 194 } 195 196 /** 197 * Sets the surface flinger's Daltonization mode. This adjusts the color 198 * space to correct for or simulate various types of color blindness. 199 * 200 * @param mode new Daltonization mode 201 */ 202 private static void nativeSetDaltonizerMode(int mode) { 203 try { 204 final IBinder flinger = ServiceManager.getService("SurfaceFlinger"); 205 if (flinger != null) { 206 final Parcel data = Parcel.obtain(); 207 data.writeInterfaceToken("android.ui.ISurfaceComposer"); 208 data.writeInt(mode); 209 flinger.transact(1014, data, null, 0); 210 data.recycle(); 211 } 212 } catch (RemoteException ex) { 213 Slog.e(LOG_TAG, "Failed to set Daltonizer mode", ex); 214 } 215 } 216 217 /** 218 * Sets the surface flinger's color transformation as a 4x4 matrix. If the 219 * matrix is null, color transformations are disabled. 220 * 221 * @param m the float array that holds the transformation matrix, or null to 222 * disable transformation 223 */ 224 private static void nativeSetColorTransform(float[] m) { 225 try { 226 final IBinder flinger = ServiceManager.getService("SurfaceFlinger"); 227 if (flinger != null) { 228 final Parcel data = Parcel.obtain(); 229 data.writeInterfaceToken("android.ui.ISurfaceComposer"); 230 if (m != null) { 231 data.writeInt(1); 232 for (int i = 0; i < 16; i++) { 233 data.writeFloat(m[i]); 234 } 235 } else { 236 data.writeInt(0); 237 } 238 flinger.transact(1015, data, null, 0); 239 data.recycle(); 240 } 241 } catch (RemoteException ex) { 242 Slog.e(LOG_TAG, "Failed to set color transform", ex); 243 } 244 } 245 246} 247