概述
平台: RK3568 + Android 11
在一些特殊的日子,如默哀日、灾难日,纪念日,哀悼日等,许多的APP、网页、海报等都开始使用黑白色主题。Android 的全局黑白实现方案,可以考虑使用模拟颜色空间的方法。
借助硬件加速渲染选项,您可以利用基于硬件的选项(如 GPU、硬件层和多重采样抗锯齿 (MSAA)针对目标硬件平台优化应用。
点按模拟颜色空间可以更改整个设备界面的配色方案。此设置下面的选项是指色盲类型。可选项如下:
- 已停用(无模拟配色方案)
- 全色盲(配色方案限于黑色、白色和灰色)
- 绿色弱视(影响显示红色和绿色)
- 红色弱视(影响显示红色和绿色)
- 蓝色弱视(影响显示蓝色和黄色)
其中“红色弱视”是指红绿色盲,红色弱视;“绿色弱视”(图 8 所示)是指红绿色盲,绿色弱视。
如果您在模拟颜色空间中截取屏幕截图,它们会正常显示,如同没有更改配色方案。
实现
在设置的开发选项中可以找到: 设置 > 系统 > 开发者选项 > 模拟颜色空间,
文本来源
rk3568_a11$ grep -r "模拟颜色空间" frameworks/base/packages/SettingsLib/
frameworks/base/packages/SettingsLib/res/values-zh-rCN/strings.xml
<string name="simulate_color_space" msgid="1206503300335835151">"模拟颜色空间"</string>
<string name="daltonizer_mode_monochromacy" msgid="362060873835885014">"全色盲"</string>
<string name="daltonizer_mode_deuteranomaly" msgid="3507284319584683963">"绿色弱视(红绿不分)"</string>
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"红色弱视(红绿不分)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"蓝色弱视(蓝黄不分)"</string>
rk3568_a11$ grep -r "simulate_color_space" packages/apps/Settings
packages/apps/Settings/tests/robotests/src/com/android/settings/development/SimulateColorSpacePreferenceControllerTest.java: mListValues = mContext.getResources().getStringArray(R.array.simulate_color_space_values);
packages/apps/Settings/src/com/android/settings/development/SimulateColorSpacePreferenceController.java: private static final String SIMULATE_COLOR_SPACE = "simulate_color_space";
packages/apps/Settings/res/xml/development_settings.xml: android:entries="@array/simulate_color_space_entries"
packages/apps/Settings/res/xml/development_settings.xml: android:entryValues="@array/simulate_color_space_values"
packages/apps/Settings/res/xml/development_settings.xml: android:key="simulate_color_space"
packages/apps/Settings/res/xml/development_settings.xml: android:title="@string/simulate_color_space" />
packages/apps/Settings/res/xml/development_settings.xml 开发者选项
<ListPreference
android:entries="@array/simulate_color_space_entries"
android:entryValues="@array/simulate_color_space_values"
android:key="simulate_color_space"
android:summary="%s"
android:title="@string/simulate_color_space" />
对应的模式的值
frameworks/base/packages/SettingsLib/res/values/arrays.xml
<!-- Display color space adjustment modes for developers -->
<string-array name="simulate_color_space_entries" translatable="false">
<item>@string/daltonizer_mode_disabled</item>
<item>@string/daltonizer_mode_monochromacy</item>
<item>@string/daltonizer_mode_deuteranomaly</item>
<item>@string/daltonizer_mode_protanomaly</item>
<item>@string/daltonizer_mode_tritanomaly</item>
</string-array>
<!-- Values for display color space adjustment modes for developers -->
<string-array name="simulate_color_space_values" translatable="false">
<item>-1</item>
<item>0</item>
<item>2</item>
<item>1</item>
<item>3</item>
</string-array>
修改系统设置值, 主要是使能和模式
packages/apps/Settings/src/com/android/settings/development/SimulateColorSpacePreferenceController.java
private void writeSimulateColorSpace(Object value) {
final ContentResolver cr = mContext.getContentResolver();
final int newMode = Integer.parseInt(value.toString());
if (newMode < 0) {
Settings.Secure.putInt(cr, Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED,
SETTING_VALUE_OFF);
} else {
Settings.Secure.putInt(cr, Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED,
SETTING_VALUE_ON);
Settings.Secure.putInt(cr, Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER, newMode);
}
}
服务里监听设置项变化并执行响应
frameworks/base/services/core/java/com/android/server/display/color/ColorDisplayService.java
/**
* Apply the accessibility daltonizer transform based on the settings value.
*/
private void onAccessibilityDaltonizerChanged() {
if (mCurrentUser == UserHandle.USER_NULL) {
return;
}
final int daltonizerMode = isAccessiblityDaltonizerEnabled()
? Secure.getIntForUser(getContext().getContentResolver(),
Secure.ACCESSIBILITY_DISPLAY_DALTONIZER,
AccessibilityManager.DALTONIZER_CORRECT_DEUTERANOMALY, mCurrentUser)
: AccessibilityManager.DALTONIZER_DISABLED;
final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
if (daltonizerMode == AccessibilityManager.DALTONIZER_SIMULATE_MONOCHROMACY) {
// Monochromacy isn't supported by the native Daltonizer implementation; use grayscale.
dtm.setColorMatrix(DisplayTransformManager.LEVEL_COLOR_MATRIX_GRAYSCALE,
MATRIX_GRAYSCALE);
dtm.setDaltonizerMode(AccessibilityManager.DALTONIZER_DISABLED);
} else {
dtm.setColorMatrix(DisplayTransformManager.LEVEL_COLOR_MATRIX_GRAYSCALE, null);
dtm.setDaltonizerMode(daltonizerMode);
}
}
frameworks/base/services/core/java/com/android/server/display/color/DisplayTransformManager.java
private static final int SURFACE_FLINGER_TRANSACTION_DALTONIZER = 1014
/**
* Sets and applies a current color transform matrix for a given level.
* <p>
* Note: all color transforms are first composed to a single matrix in ascending order based on
* level before being applied to the display.
*
* @param level the level used to identify and compose the color transform (low -> high)
* @param value the 4x4 color transform matrix (in column-major order), or {@code null} to
* remove the color transform matrix associated with the provided level
*/
public void setColorMatrix(int level, float[] value) {
if (value != null && value.length != 16) {
throw new IllegalArgumentException("Expected length: 16 (4x4 matrix)"
+ ", actual length: " + value.length);
}
synchronized (mColorMatrix) {
final float[] oldValue = mColorMatrix.get(level);
if (!Arrays.equals(oldValue, value)) {
if (value == null) {
mColorMatrix.remove(level);
} else if (oldValue == null) {
mColorMatrix.put(level, Arrays.copyOf(value, value.length));
} else {
System.arraycopy(value, 0, oldValue, 0, value.length);
}
// Update the current color transform.
applyColorMatrix(computeColorMatrixLocked());
}
}
}
/**
* Sets the current Daltonization mode. This adjusts the color space to correct for or simulate
* various types of color blindness.
*
* @param mode the new Daltonization mode, or -1 to disable
*/
public void setDaltonizerMode(int mode) {
synchronized (mDaltonizerModeLock) {
if (mDaltonizerMode != mode) {
mDaltonizerMode = mode;
applyDaltonizerMode(mode);
}
}
}
/**
* Propagates the provided Daltonization mode to the SurfaceFlinger.
*/
private static void applyDaltonizerMode(int mode) {
final Parcel data = Parcel.obtain();
data.writeInterfaceToken("android.ui.ISurfaceComposer");
data.writeInt(mode);
try {
sFlinger.transact(SURFACE_FLINGER_TRANSACTION_DALTONIZER, data, null, 0);
} catch (RemoteException ex) {
Slog.e(TAG, "Failed to set Daltonizer mode", ex);
} finally {
data.recycle();
}
}
传到SurfaceFlinger
frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
uint32_t flags) {
//............
switch (code) {
case 1014: {
Mutex::Autolock _l(mStateLock);
// daltonize
n = data.readInt32();
switch (n % 10) {
case 1:
mDaltonizer.setType(ColorBlindnessType::Protanomaly);
break;
case 2:
mDaltonizer.setType(ColorBlindnessType::Deuteranomaly);
break;
case 3:
mDaltonizer.setType(ColorBlindnessType::Tritanomaly);
break;
default:
mDaltonizer.setType(ColorBlindnessType::None);
break;
}
if (n >= 10) {
mDaltonizer.setMode(ColorBlindnessMode::Correction);
} else {
mDaltonizer.setMode(ColorBlindnessMode::Simulation);
}
updateColorMatrixLocked();
return NO_ERROR;
}
//........................
关于 Settings > 色彩校正
实现的原理与黑白色是一样的:
packages/apps/Settings/res/xml/accessibility_daltonizer_settings.xml
<PreferenceCategory
android:title="@string/daltonizer_type"
android:key="daltonizer_mode_category" >
<com.android.settingslib.widget.RadioButtonPreference
android:key="daltonizer_mode_deuteranomaly"
android:persistent="false"
android:summary="@string/daltonizer_mode_deuteranomaly_summary"
android:title="@string/daltonizer_mode_deuteranomaly_title" />
<com.android.settingslib.widget.RadioButtonPreference
android:key="daltonizer_mode_protanomaly"
android:persistent="false"
android:summary="@string/daltonizer_mode_protanomaly_summary"
android:title="@string/daltonizer_mode_protanomaly_title" />
<com.android.settingslib.widget.RadioButtonPreference
android:key="daltonizer_mode_tritanomaly"
android:persistent="false"
android:summary="@string/daltonizer_mode_tritanomaly_summary"
android:title="@string/daltonizer_mode_tritanomaly_title" />
</PreferenceCategory>
packages/apps/Settings/src/com/android/settings/accessibility/ToggleDaltonizerPreferenceFragment.java
@Override
protected int getPreferenceScreenResId() {
return R.xml.accessibility_daltonizer_settings;
}
packages/apps/Settings/src/com/android/settings/accessibility/DaltonizerRadioButtonPreferenceController.java
private static final String TYPE = Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER;
private void putSecureString(String name, String value) {
Settings.Secure.putString(mContentResolver, name, value);
}
private void handlePreferenceChange(String value) {
putSecureString(TYPE, value);
}
APP 如何调用?
普通APP没有调用的权限, 两种调用方法:
- App拥有system uid
final android.content.ContentResolver cr = context.getContentResolver();
final int newMode = on ? 0 : -1;
if (newMode < 0) {
android.provider.Settings.Secure.putInt(cr, "accessibility_display_daltonizer_enabled",
0);
} else {
android.provider.Settings.Secure.putInt(cr, "accessibility_display_daltonizer_enabled",
1);
//public static final String ACCESSIBILITY_DISPLAY_DALTONIZER =
// "accessibility_display_daltonizer";
android.provider.Settings.Secure.putInt(cr, "accessibility_display_daltonizer", newMode);
}
- 平台已ROOT, 执行命令
settings put secure accessibility_display_daltonizer_enabled 1
settings put secure accessibility_display_daltonizer 0
参考
安卓APP全局黑白化实现方案
Android全局设置APP为黑白模式的两种方案
配置设备上的开发者选项