概要
平台: RK3568 + Android 11
沈黙の日、災害の日、記念日、追悼の日などの特別な日には、多くのアプリ、Web ページ、ポスターなどが白黒の テーマを使用し始めます。Android のグローバルな白黒実装の場合は、色空間をシミュレートする方法の使用を検討できます。
ハードウェア アクセラレーション レンダリング オプションを使用すると、GPU、ハードウェア レイヤー、マルチサンプル アンチエイリアシング (MSAA) などのハードウェア ベースのオプションを利用して、ターゲット ハードウェア プラットフォームに合わせてアプリケーションを最適化できます。
[色空間のシミュレート]をタップして、デバイス インターフェイス全体の配色を変更します。この設定の下にあるオプションは、色覚異常のタイプを示します。オプションは次のとおりです。
- 無効 (シミュレートされたカラースキームなし)
- アクロマトプシア(配色は黒、白、グレーに限定)
- 第二色 (赤と緑の表示に影響を与える)
- Protomaly (赤と緑の表示に影響します)
- 顕色異常(青と黄色の表示に影響を与える)
このうち、「前色覚異常」は赤緑色盲、第一色覚異常を指し、「第二色覚異常」(図 8 に示す)は赤緑色覚異常、第二色覚異常を指します。
シミュレートされた色空間でスクリーンショットを撮ると、配色が変更されていないかのように正常に表示されます。
達成
これは、設定の開発オプションにあります: [設定] > [システム] > [開発者向けオプション] > [シミュレーション色空間]
テキストソース
rk3568_a11$ grep -r "模拟颜色空间" frameworks/base/packages/SettingsLib/
フレームワーク/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" />
package/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" />
対応するモードの値
フレームワーク/ベース/パッケージ/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>
システム設定を変更します(主に有効化とモード)
パッケージ/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 に渡す
フレームワーク/ネイティブ/サービス/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;
}
//........................
設定 > 色補正について
実装の原則は白黒と同じです。
パッケージ/apps/設定/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>
パッケージ/apps/Settings/src/com/android/settings/accessibility/ToggleDaltonizerPreferenceFragment.java
@Override
protected int getPreferenceScreenResId() {
return R.xml.accessibility_daltonizer_settings;
}
パッケージ/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には呼び出し権限がありません。呼び出し方法は2つあります。
- アプリにはシステム 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
参考
Android APP グローバル白黒実装スキーム
Android グローバル設定 APP を白黒モードに設定する 2 つのソリューション デバイス
で開発者向けオプションを構成する