Android 全局黑白化-模拟颜色空间

概述

平台: RK3568 + Android 11

     在一些特殊的日子,如默哀日、灾难日,纪念日,哀悼日等,许多的APP、网页、海报等都开始使用黑白色主题。Android 的全局黑白实现方案,可以考虑使用模拟颜色空间的方法。

Android 全局黑白化-模拟颜色空间_第1张图片

      借助硬件加速渲染选项,您可以利用基于硬件的选项(如 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

    
    <string-array name="simulate_color_space_entries" translatable="false">
        <item>@string/daltonizer_mode_disableditem>
        <item>@string/daltonizer_mode_monochromacyitem>
        <item>@string/daltonizer_mode_deuteranomalyitem>
        <item>@string/daltonizer_mode_protanomalyitem>
        <item>@string/daltonizer_mode_tritanomalyitem>
    string-array>

    
    <string-array name="simulate_color_space_values" translatable="false">
        <item>-1item>
        <item>0item>
        <item>2item>
        <item>1item>
        <item>3item>
    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.
     * 

* 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 > 色彩校正

实现的原理与黑白色是一样的:
Android 全局黑白化-模拟颜色空间_第2张图片

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没有调用的权限, 两种调用方法:

  1. 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);
        }
  1. 平台已ROOT, 执行命令
settings put secure accessibility_display_daltonizer_enabled 1
settings put secure accessibility_display_daltonizer 0

参考

安卓APP全局黑白化实现方案
Android全局设置APP为黑白模式的两种方案
配置设备上的开发者选项

你可能感兴趣的:(android,android,黑白,模拟颜色空间)