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

概述

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

  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为黑白模式的两种方案
配置设备上的开发者选项

猜你喜欢

转载自blog.csdn.net/ansondroider/article/details/131112051