美文网首页色彩
Android Q 色彩(颜色)模式解析(一)

Android Q 色彩(颜色)模式解析(一)

作者: 夕月风 | 来源:发表于2019-08-29 14:25 被阅读0次

    [TOC]

    Android 色彩(颜色)模式解析(一)

    Android Q 在系统设置中提供了可设置的色彩模式,当然这功能很多厂商早就有了~,落后归落后,我们还是看看Android是怎么实现的!

    Android Q提供了4种色彩模式:

    • Natural 自然色
    • Boosted 效果增强
    • Saturated 饱和色
    • Adaptive 自动调节

    下面我们就结合实际代码,看看具体的实现和流程!

    FrameWork色彩模式的定义及实现

    为了实现色彩模式的切换,Android Framework设计了ColorDisplayManager及对应的服务,提供可切换的色彩模式和对应的设置接口。四种色彩模式对应的值如下:

        public static final int COLOR_MODE_NATURAL = 0;
    
        public static final int COLOR_MODE_BOOSTED = 1;
    
        public static final int COLOR_MODE_SATURATED = 2;
    
        public static final int COLOR_MODE_AUTOMATIC = 3;
    

    Settings中通过ColorDisplayManager的setColorMode接口进行色彩模式的切换,对应的代码如下:

    * packages/apps/Settings/src/com/android/settings/display/ColorModePreferenceFragment.java
    
        @Override
        protected boolean setDefaultKey(String key) {
            switch (key) {
                case KEY_COLOR_MODE_NATURAL:
                    mColorDisplayManager.setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL);
                    break;
                case KEY_COLOR_MODE_BOOSTED:
                    mColorDisplayManager.setColorMode(ColorDisplayManager.COLOR_MODE_BOOSTED);
                    break;
                case KEY_COLOR_MODE_SATURATED:
                    mColorDisplayManager.setColorMode(ColorDisplayManager.COLOR_MODE_SATURATED);
                    break;
                case KEY_COLOR_MODE_AUTOMATIC:
                    mColorDisplayManager.setColorMode(ColorDisplayManager.COLOR_MODE_AUTOMATIC);
                    break;
            }
            return true;
        }
    

    在ColorDisplayManager中,设计了一个内部类ColorDisplayManagerInternal,通过内部类和对应的系统服务COLOR_DISPLAY_SERVICE进行交互。

    *  frameworks/base/core/java/android/hardware/display/ColorDisplayManager.java
    
        public void setColorMode(int colorMode) {
            mManager.setColorMode(colorMode);
        }
    

    对应的服务实现如下:

    * frameworks/base/services/core/java/com/android/server/display/color/ColorDisplayService.java
    
        private void setColorModeInternal(@ColorMode int colorMode) {
            if (!isColorModeAvailable(colorMode)) {
                throw new IllegalArgumentException("Invalid colorMode: " + colorMode);
            }
            System.putIntForUser(getContext().getContentResolver(), System.DISPLAY_COLOR_MODE,
                    colorMode,
                    mCurrentUser);
        }
    

    在色彩显示服务中,只是将需要的色彩模式对应的值写到了,系统设置的数据库中,键值DISPLAY_COLOR_MODE。

    其实,看多了Framework的代码,我们就会知道,肯定会有一个ContentObserver来监听这个数据的变化的,在ColorDisplayService的setUp函数中:

        private void setUp() {
            Slog.d(TAG, "setUp: currentUser=" + mCurrentUser);
    
            // Listen for external changes to any of the settings.
            if (mContentObserver == null) {
                mContentObserver = new ContentObserver(new Handler(DisplayThread.get().getLooper())) {
                    @Override
                    public void onChange(boolean selfChange, Uri uri) {
                        super.onChange(selfChange, uri);
    
                        final String setting = uri == null ? null : uri.getLastPathSegment();
                        if (setting != null) {
                            switch (setting) {
                                ... ...
                                case System.DISPLAY_COLOR_MODE:
                                    onDisplayColorModeChanged(getColorModeInternal());
    

    当设置中,切换色彩模式,修改了系统设置中的DISPLAY_COLOR_MODE值后,这个observer就会触发DISPLAY_COLOR_MODE的onChange,最后通过onDisplayColorModeChanged函数来处理。

    * frameworks/base/services/core/java/com/android/server/display/color/ColorDisplayService.java
    
        private void onDisplayColorModeChanged(int mode) {
            if (mode == NOT_SET) {
                return;
            }
    
            mNightDisplayTintController.cancelAnimator();
            mDisplayWhiteBalanceTintController.cancelAnimator();
    
            if (mNightDisplayTintController.isAvailable(getContext())) {
                mNightDisplayTintController
                        .setUp(getContext(), DisplayTransformManager.needsLinearColorMatrix(mode));
                mNightDisplayTintController
                        .setMatrix(mNightDisplayTintController.getColorTemperatureSetting());
            }
    
            updateDisplayWhiteBalanceStatus();
    
            final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
            dtm.setColorMode(mode, mNightDisplayTintController.getMatrix());
        }
    

    这里涉及到夜光屏和白平衡,说白了,这些的实现都是跟色彩和亮度有关。我们先留下夜光屏幕和白平衡,单看色彩模式的流程。色彩模式,通过 DisplayTransformManager 的setColorMode接口继续往下传:

    * frameworks/base/services/core/java/com/android/server/display/color/DisplayTransformManager.java
    
        public boolean setColorMode(int colorMode, float[] nightDisplayMatrix) {
            if (colorMode == ColorDisplayManager.COLOR_MODE_NATURAL) {
                applySaturation(COLOR_SATURATION_NATURAL);
                setDisplayColor(DISPLAY_COLOR_MANAGED);
            } else if (colorMode == ColorDisplayManager.COLOR_MODE_BOOSTED) {
                applySaturation(COLOR_SATURATION_BOOSTED);
                setDisplayColor(DISPLAY_COLOR_MANAGED);
            } else if (colorMode == ColorDisplayManager.COLOR_MODE_SATURATED) {
                applySaturation(COLOR_SATURATION_NATURAL);
                setDisplayColor(DISPLAY_COLOR_UNMANAGED);
            } else if (colorMode == ColorDisplayManager.COLOR_MODE_AUTOMATIC) {
                applySaturation(COLOR_SATURATION_NATURAL);
                setDisplayColor(DISPLAY_COLOR_ENHANCED);
            }
            setColorMatrix(LEVEL_COLOR_MATRIX_NIGHT_DISPLAY, nightDisplayMatrix);
    
            updateConfiguration();
    
            return true;
        }
    

    重点来了,前面我们说到的4种色彩模式,主要通过两个参数的调节来实现的

    • Saturation饱和度
      饱和度两种可选模式:自然模式和增强模式
        private static final float COLOR_SATURATION_NATURAL = 1.0f;
        private static final float COLOR_SATURATION_BOOSTED = 1.1f;
    
    • DisplayColor显示颜色
      显示颜色有3种模式:Managed,UnManaged以及增强模式
        private static final int DISPLAY_COLOR_MANAGED = 0;
        private static final int DISPLAY_COLOR_UNMANAGED = 1;
        private static final int DISPLAY_COLOR_ENHANCED = 2;
    

    我们的4中色彩模式:

    ** COLOR_SATURATION_NATURAL COLOR_SATURATION_BOOSTED
    DISPLAY_COLOR_MANAGED 自然色 COLOR_MODE_NATURAL 效果增强 COLOR_MODE_BOOSTED
    DISPLAY_COLOR_UNMANAGED 饱和色 COLOR_MODE_SATURATED
    DISPLAY_COLOR_ENHANCED 自动调节 COLOR_MODE_AUTOMATIC

    这两个参数的设置是通过binder直接设置到SurfaceFlinger的,对应的Binder 命令分别为SURFACE_FLINGER_TRANSACTION_SATURATIONSURFACE_FLINGER_TRANSACTION_DISPLAY_COLOR。相应的代码如下:
    饱和度的设置通过applySaturation函数

    * frameworks/base/services/core/java/com/android/server/display/color/DisplayTransformManager.java
    
        private void applySaturation(float saturation) {
            SystemProperties.set(PERSISTENT_PROPERTY_SATURATION, Float.toString(saturation));
            final IBinder flinger = ServiceManager.getService(SURFACE_FLINGER);
            if (flinger != null) {
                final Parcel data = Parcel.obtain();
                data.writeInterfaceToken("android.ui.ISurfaceComposer");
                data.writeFloat(saturation);
                try {
                    flinger.transact(SURFACE_FLINGER_TRANSACTION_SATURATION, data, null, 0);
                } catch (RemoteException ex) {
                    Slog.e(TAG, "Failed to set saturation", ex);
                } finally {
                    data.recycle();
                }
            }
        }
    

    显示颜色的设置通过setDisplayColor函数

        private void setDisplayColor(int color) {
            SystemProperties.set(PERSISTENT_PROPERTY_DISPLAY_COLOR, Integer.toString(color));
            final IBinder flinger = ServiceManager.getService(SURFACE_FLINGER);
            if (flinger != null) {
                final Parcel data = Parcel.obtain();
                data.writeInterfaceToken("android.ui.ISurfaceComposer");
                data.writeInt(color);
                try {
                    flinger.transact(SURFACE_FLINGER_TRANSACTION_DISPLAY_COLOR, data, null, 0);
                } catch (RemoteException ex) {
                    Slog.e(TAG, "Failed to set display color", ex);
                } finally {
                    data.recycle();
                }
            }
        }
    

    小结一下,Android提供了4种色彩模式:自然色,效果增强,饱和色和自动调节。而Framework中,是通过两个参数来实现的:饱和度和显示颜色,具体实现,,,往下看!

    SurfaceFlinger对色彩模式的支持和实现

    通过前面的分析,我们其实只需关心饱和度和显示颜色!SurfaceFlinger中,饱和度用饱和因子mGlobalSaturationFactor来定义,显示颜色用mDisplayColorSetting进行描述。

    status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
                                        uint32_t flags) {
        ... ...
        status_t err = BnSurfaceComposer::onTransact(code, data, reply, flags);
        if (err == UNKNOWN_TRANSACTION || err == PERMISSION_DENIED) {
            ... ...
            int n;
            switch (code) {
                ... ...
                case 1022: { // Set saturation boost
                    Mutex::Autolock _l(mStateLock);
                    mGlobalSaturationFactor = std::max(0.0f, std::min(data.readFloat(), 2.0f));
    
                    updateColorMatrixLocked();
    
                    ALOGE("xm-gfx: 1022 mGlobalSaturationFactor:%f\n", mGlobalSaturationFactor);
                    return NO_ERROR;
                }
                case 1023: { // Set native mode
                    mDisplayColorSetting = static_cast<DisplayColorSetting>(data.readInt32());
                    invalidateHwcGeometry();
                    repaintEverything();
                    ALOGE("xm-gfx: 1023 mDisplayColorSetting:%d\n", mDisplayColorSetting);
                    return NO_ERROR;
                }
    

    饱和因子是一个float值,默认为1.0,系统最初始的值,可以通过属性persist.sys.sf.color_saturation进行设置。饱和因子影响到的是显示的颜色矩阵,具体的算法在这个函数中:

    void SurfaceFlinger::updateColorMatrixLocked() {
        mat4 colorMatrix;
        if (mGlobalSaturationFactor != 1.0f) {
            // Rec.709 luma coefficients
            float3 luminance{0.213f, 0.715f, 0.072f};
            luminance *= 1.0f - mGlobalSaturationFactor;
            mat4 saturationMatrix = mat4(
                vec4{luminance.r + mGlobalSaturationFactor, luminance.r, luminance.r, 0.0f},
                vec4{luminance.g, luminance.g + mGlobalSaturationFactor, luminance.g, 0.0f},
                vec4{luminance.b, luminance.b, luminance.b + mGlobalSaturationFactor, 0.0f},
                vec4{0.0f, 0.0f, 0.0f, 1.0f}
            );
            colorMatrix = mClientColorMatrix * saturationMatrix * mDaltonizer();
        } else {
            colorMatrix = mClientColorMatrix * mDaltonizer();
        }
    
        if (mCurrentState.colorMatrix != colorMatrix) {
            mCurrentState.colorMatrix = colorMatrix;
            mCurrentState.colorMatrixChanged = true;
            setTransactionFlags(eTransactionNeeded);
        }
    }
    

    根据饱和因子,生成一个饱和度的矩阵saturationMatrix,再和其他的颜色矩阵相乘,得到需要的矩阵。至于这个矩阵colorMatrix怎么生效的,我们稍后再看!

    显示颜色mDisplayColorSetting,最后值可以通过persist.sys.sf.native_mode属性来设置。

    看屏幕是否支持颜色管理

    SurfaceFlinger定义useColorManagement来描述SurfaceFlinger是否管理颜色。

    bool use_color_management(bool defaultValue) {
        auto tmpuseColorManagement = SurfaceFlingerProperties::use_color_management();
        auto tmpHasHDRDisplay = SurfaceFlingerProperties::has_HDR_display();
        auto tmpHasWideColorDisplay = SurfaceFlingerProperties::has_wide_color_display();
    
        auto tmpuseColorManagementVal = tmpuseColorManagement.has_value() ? *tmpuseColorManagement :
            defaultValue;
        auto tmpHasHDRDisplayVal = tmpHasHDRDisplay.has_value() ? *tmpHasHDRDisplay :
            defaultValue;
        auto tmpHasWideColorDisplayVal = tmpHasWideColorDisplay.has_value() ? *tmpHasWideColorDisplay :
            defaultValue;
    
        return tmpuseColorManagementVal || tmpHasHDRDisplayVal || tmpHasWideColorDisplayVal;
    }
    

    第一次看这个代码的时候比较奇怪,其实这个就是对属性访问的一个封装,对应的属性访问和接口用sysprop进行描述,定义在SurfaceFlingerProperties.sysprop中,上述3个变量对应的3个属性如下:

    ro.surface_flinger.use_color_management
    ro.surface_flinger.has_HDR_display
    ro.surface_flinger.has_wide_color_display
    

    我们来看一个属性接口的描述:

    * frameworks/native/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
    prop {
        api_name: "has_wide_color_display"
        type: Boolean
        scope: Internal
        access: Readonly
        prop_name: "ro.surface_flinger.has_wide_color_display"
    }
    

    sysprop会被编译成cpp文件!生产的文件在:

    out/soong/.intermediates/frameworks/native/services/surfaceflinger/sysprop/libSurfaceFlingerProperties/android_arm64_armv8-a_core_static/gen/sysprop/SurfaceFlingerProperties.sysprop.cpp
    

    has_wide_color_display对应的实现如下:

    std::optional<bool> has_wide_color_display() {
        return GetProp<std::optional<bool>>("ro.surface_flinger.has_wide_color_display");
    }
    

    屏幕的ColorProfile

    颜色管理,是针对屏幕的,不是对单个Layer的!每次一次开始合成时,都会去做屏幕颜色的处理,在calculateWorkingSet函数中。

    void SurfaceFlinger::calculateWorkingSet() {
        ... ...
    
        // Set the per-frame data
        for (const auto& [token, displayDevice] : mDisplays) {
            auto display = displayDevice->getCompositionDisplay();
            const auto displayId = display->getId();
            if (!displayId) {
                continue;
            }
            auto* profile = display->getDisplayColorProfile();
    
            if (mDrawingState.colorMatrixChanged) {
                display->setColorTransform(mDrawingState.colorMatrix);
            }
            Dataspace targetDataspace = Dataspace::UNKNOWN;
            if (useColorManagement) {
                ColorMode colorMode;
                RenderIntent renderIntent;
                pickColorMode(displayDevice, &colorMode, &targetDataspace, &renderIntent);
                display->setColorMode(colorMode, targetDataspace, renderIntent);
            }
    

    SurfaceFlinger中,Profile封装了传输给显示屏幕颜色的所有的状态和功能!ColorMode只是显示屏众多特性中的一个。SurfaceFlinger中用ColorModeValue进行描述:

    * frameworks/native/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DisplayColorProfile.h
    
        struct ColorModeValue {
            ui::Dataspace dataspace;
            ui::ColorMode colorMode;
            ui::RenderIntent renderIntent;
        };
    

    ui的定义在hardware/interfaces/graphics/common中。目前是1.3版本!

    我们来看看几个比较常用的:

    • Dataspace
    * hardware/interfaces/graphics/common/1.0/types.hal
    
    @export(name="android_dataspace_t", value_prefix="HAL_DATASPACE_")
    enum Dataspace : int32_t {
        /**
         * Default-assumption data space, when not explicitly specified.
         *
         * It is safest to assume the buffer is an image with sRGB primaries and
         * encoding ranges, but the consumer and/or the producer of the data may
         * simply be using defaults. No automatic gamma transform should be
         * expected, except for a possible display gamma transform when drawn to a
         * screen.
         */
        UNKNOWN = 0x0,
    
        ... ...
    
        /**
         * Transfer characteristic curve:
         *  E = L
         *      L - luminance of image 0 <= L <= 1 for conventional colorimetry
         *      E - corresponding electrical signal
         */
        TRANSFER_LINEAR = 1 << TRANSFER_SHIFT,
    
        /**
         * Transfer characteristic curve:
         *
         * E = 1.055 * L^(1/2.4) - 0.055  for 0.0031308 <= L <= 1
         *   = 12.92 * L                  for 0 <= L < 0.0031308
         *     L - luminance of image 0 <= L <= 1 for conventional colorimetry
         *     E - corresponding electrical signal
         */
        TRANSFER_SRGB = 2 << TRANSFER_SHIFT,
        ... ...
    
        /**
         * sRGB gamma encoding:
         *
         * The red, green and blue components are stored in sRGB space, and
         * converted to linear space when read, using the SRGB transfer function
         * for each of the R, G and B components. When written, the inverse
         * transformation is performed.
         *
         * The alpha component, if present, is always stored in linear space and
         * is left unmodified when read or written.
         *
         * Use full range and BT.709 standard.
         */
        SRGB = 0x201, // deprecated, use V0_SRGB
    
        V0_SRGB = STANDARD_BT709 | TRANSFER_SRGB | RANGE_FULL,
    
        ... ...
    
        /**
         * Display P3
         *
         * Use same primaries and white-point as DCI-P3
         * but sRGB transfer function.
         */
        DISPLAY_P3 = STANDARD_DCI_P3 | TRANSFER_SRGB | RANGE_FULL,
    };
    
    
    • ColorMode
    * hardware/interfaces/graphics/common/1.0/types.hal
    
    @export(name="android_color_mode_t", value_prefix="HAL_COLOR_MODE_")
    enum ColorMode : int32_t {
        /**
         * DEFAULT is the "native" gamut of the display.
         * White Point: Vendor/OEM defined
         * Panel Gamma: Vendor/OEM defined (typically 2.2)
         * Rendering Intent: Vendor/OEM defined (typically 'enhanced')
         */
        NATIVE = 0,
    
        ... ...
    
        /**
         * SRGB corresponds with display settings that implement
         * the sRGB color space. Uses the same primaries as ITU-R Recommendation
         * BT.709
         * Rendering Intent: Colorimetric
         * Primaries:
         *                  x       y
         *  green           0.300   0.600
         *  blue            0.150   0.060
         *  red             0.640   0.330
         *  white (D65)     0.3127  0.3290
         *
         * PC/Internet (sRGB) Inverse Gamma Correction (IGC):
         *
         *  if Vnonlinear ≤ 0.03928
         *    Vlinear = Vnonlinear / 12.92
         *  else
         *    Vlinear = ((Vnonlinear + 0.055)/1.055) ^ 2.4
         *
         * PC/Internet (sRGB) Gamma Correction (GC):
         *
         *  if Vlinear ≤ 0.0031308
         *    Vnonlinear = 12.92 * Vlinear
         *  else
         *    Vnonlinear = 1.055 * (Vlinear)^(1/2.4) – 0.055
         */
        SRGB = 7,
    
        ... ...
    
        /**
         * DISPLAY_P3 is a color space that uses the DCI_P3 primaries,
         * the D65 white point and the SRGB transfer functions.
         * Rendering Intent: Colorimetric
         * Primaries:
         *                  x       y
         *  green           0.265   0.690
         *  blue            0.150   0.060
         *  red             0.680   0.320
         *  white (D65)     0.3127  0.3290
         *
         * PC/Internet (sRGB) Gamma Correction (GC):
         *
         *  if Vlinear ≤ 0.0030186
         *    Vnonlinear = 12.92 * Vlinear
         *  else
         *    Vnonlinear = 1.055 * (Vlinear)^(1/2.4) – 0.055
         *
         * Note: In most cases sRGB transfer function will be fine.
         */
        DISPLAY_P3 = 9
    };
    
    • RenderIntent
    * hardware/interfaces/graphics/common/1.1/types.hal
    
    @export(name="android_render_intent_v1_1_t", value_prefix="HAL_RENDER_INTENT_",
            export_parent="false")
    enum RenderIntent : int32_t {
        /**
         * Colors in the display gamut are unchanged. Colors out of the display
         * gamut are hard-clipped.
         *
         * This implies that the display must have been calibrated unless
         * ColorMode::NATIVE is the only supported color mode.
         */
        COLORIMETRIC = 0,
    
        /**
         * Enhance colors that are in the display gamut. Colors out of the display
         * gamut are hard-clipped.
         *
         * The enhancement typically picks the biggest standard color space (e.g.
         * DCI-P3) that is narrower than the display gamut and stretches it to the
         * display gamut. The stretching is recommended to preserve skin tones.
         */
        ENHANCE = 1,
    
        /**
         * Tone map high-dynamic-range colors to the display's dynamic range. The
         * dynamic range of the colors are communicated separately. After tone
         * mapping, the mapping to the display gamut is as defined in
         * COLORIMETRIC.
         */
        TONE_MAP_COLORIMETRIC = 2,
    
        /**
         * Tone map high-dynamic-range colors to the display's dynamic range. The
         * dynamic range of the colors are communicated separately. After tone
         * mapping, the mapping to the display gamut is as defined in ENHANCE.
         *
         * The tone mapping step and the enhancing step must match
         * TONE_MAP_COLORIMETRIC and ENHANCE respectively when they are also
         * supported.
         */
        TONE_MAP_ENHANCE = 3,
    
        /*
         * Vendors are recommended to use 0x100 - 0x1FF for their own values, and
         * that must be done with subtypes defined by vendor extensions.
         */
    };
    

    SurfaceFlinger设置ColorMode的流程

    // Pick the ColorMode / Dataspace for the display device.
    void SurfaceFlinger::pickColorMode(const sp<DisplayDevice>& display, ColorMode* outMode,
                                       Dataspace* outDataSpace, RenderIntent* outRenderIntent) const {
        if (mDisplayColorSetting == DisplayColorSetting::UNMANAGED) {
            *outMode = ColorMode::NATIVE;
            *outDataSpace = Dataspace::UNKNOWN;
            *outRenderIntent = RenderIntent::COLORIMETRIC;
            return;
        }
    
        Dataspace hdrDataSpace;
        Dataspace bestDataSpace = getBestDataspace(display, &hdrDataSpace);
    
        auto* profile = display->getCompositionDisplay()->getDisplayColorProfile();
    
        switch (mForceColorMode) {
            case ColorMode::SRGB:
                bestDataSpace = Dataspace::V0_SRGB;
                break;
            case ColorMode::DISPLAY_P3:
                bestDataSpace = Dataspace::DISPLAY_P3;
                break;
            default:
                break;
        }
    
        // respect hdrDataSpace only when there is no legacy HDR support
        const bool isHdr =
                hdrDataSpace != Dataspace::UNKNOWN && !profile->hasLegacyHdrSupport(hdrDataSpace);
        if (isHdr) {
            bestDataSpace = hdrDataSpace;
        }
    
        RenderIntent intent;
        switch (mDisplayColorSetting) {
            case DisplayColorSetting::MANAGED:
            case DisplayColorSetting::UNMANAGED:
                intent = isHdr ? RenderIntent::TONE_MAP_COLORIMETRIC : RenderIntent::COLORIMETRIC;
                break;
            case DisplayColorSetting::ENHANCED:
                intent = isHdr ? RenderIntent::TONE_MAP_ENHANCE : RenderIntent::ENHANCE;
                break;
            default: // vendor display color setting
                intent = static_cast<RenderIntent>(mDisplayColorSetting);
                break;
        }
    
        profile->getBestColorMode(bestDataSpace, intent, outDataSpace, outMode, outRenderIntent);
    }
    

    可见,在选择ColorMode时,获取ColorMode,DataSpace,和RenderIntent。

    首先来看,bestDataSpace的获取,实现函数如下:

    // Returns a data space that fits all visible layers.  The returned data space
    // can only be one of
    //  - Dataspace::SRGB (use legacy dataspace and let HWC saturate when colors are enhanced)
    //  - Dataspace::DISPLAY_P3
    //  - Dataspace::DISPLAY_BT2020
    // The returned HDR data space is one of
    //  - Dataspace::UNKNOWN
    //  - Dataspace::BT2020_HLG
    //  - Dataspace::BT2020_PQ
    Dataspace SurfaceFlinger::getBestDataspace(const sp<const DisplayDevice>& display,
                                               Dataspace* outHdrDataSpace) const {
        Dataspace bestDataSpace = Dataspace::V0_SRGB;
        *outHdrDataSpace = Dataspace::UNKNOWN;
    
        for (const auto& layer : display->getVisibleLayersSortedByZ()) {
            switch (layer->getDataSpace()) {
                case Dataspace::V0_SCRGB:
                case Dataspace::V0_SCRGB_LINEAR:
                case Dataspace::BT2020:
                case Dataspace::BT2020_ITU:
                case Dataspace::BT2020_LINEAR:
                case Dataspace::DISPLAY_BT2020:
                    bestDataSpace = Dataspace::DISPLAY_BT2020;
                    break;
                case Dataspace::DISPLAY_P3:
                    bestDataSpace = Dataspace::DISPLAY_P3;
                    break;
                case Dataspace::BT2020_PQ:
                case Dataspace::BT2020_ITU_PQ:
                    bestDataSpace = Dataspace::DISPLAY_P3;
                    *outHdrDataSpace = Dataspace::BT2020_PQ;
                    break;
                case Dataspace::BT2020_HLG:
                case Dataspace::BT2020_ITU_HLG:
                    bestDataSpace = Dataspace::DISPLAY_P3;
                    // When there's mixed PQ content and HLG content, we set the HDR
                    // data space to be BT2020_PQ and convert HLG to PQ.
                    if (*outHdrDataSpace == Dataspace::UNKNOWN) {
                        *outHdrDataSpace = Dataspace::BT2020_HLG;
                    }
                    break;
                default:
                    break;
            }
        }
    
        return bestDataSpace;
    }
    

    说说实话,这个算法没有“看懂”。这里采用一个for循环,检查所有的Layer,也就是说bestDataSpace会被后面的Layer不断的覆盖... ...感觉这段法“看不懂”... Anyway,这里将选一个最合适的 bestDataSpace,并且去check有没有HDR的space hdrDataSpace。

    再回来看pickColorMode函数:

    • 可以人为的指定dataspace,通过mForceColorMode:
        property_get("persist.sys.sf.color_mode", value, "0");
        mForceColorMode = static_cast<ColorMode>(atoi(value));
    
    • 如果是Hdr,将使用HDR的Database。

    • 根据上层给下来的mDisplayColorSetting,确定 RenderIntent。

    RenderIntent isHdr Normal
    MANAGED TONE_MAP_COLORIMETRIC COLORIMETRIC
    UNMANAGED TONE_MAP_COLORIMETRIC COLORIMETRIC
    ENHANCED TONE_MAP_ENHANCE ENHANCE
    void DisplayColorProfile::getBestColorMode(Dataspace dataspace, RenderIntent intent,
                                               Dataspace* outDataspace, ColorMode* outMode,
                                               RenderIntent* outIntent) const {
        auto iter = mColorModes.find(getColorModeKey(dataspace, intent));
        if (iter != mColorModes.end()) {
            *outDataspace = iter->second.dataspace;
            *outMode = iter->second.colorMode;
            *outIntent = iter->second.renderIntent;
        } else {
            // this is unexpected on a WCG display
            if (hasWideColorGamut()) {
                ALOGE("map unknown (%s)/(%s) to default color mode",
                      dataspaceDetails(static_cast<android_dataspace_t>(dataspace)).c_str(),
                      decodeRenderIntent(intent).c_str());
            }
    
            *outDataspace = Dataspace::UNKNOWN;
            *outMode = ColorMode::NATIVE;
            *outIntent = RenderIntent::COLORIMETRIC;
        }
    }
    

    也就是从 mColorModes 中选取一个合适的模式!~ mColorModes从哪儿来的?其实,添加屏幕的时候,就会去初始化这些参数,在DisplayColorProfile的初始函数中:

    * /frameworks/native/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp
    
    DisplayColorProfile::DisplayColorProfile(DisplayColorProfileCreationArgs&& args)
          : mHasWideColorGamut(args.hasWideColorGamut),
            mSupportedPerFrameMetadata(args.supportedPerFrameMetadata) {
        populateColorModes(args.hwcColorModes);
    
        std::vector<ui::Hdr> types = args.hdrCapabilities.getSupportedHdrTypes();
        for (ui::Hdr hdrType : types) {
            switch (hdrType) {
                case ui::Hdr::HDR10_PLUS:
                    mHasHdr10Plus = true;
                    break;
                case ui::Hdr::HDR10:
                    mHasHdr10 = true;
                    break;
                case ui::Hdr::HLG:
                    mHasHLG = true;
                    break;
                case ui::Hdr::DOLBY_VISION:
                    mHasDolbyVision = true;
                    break;
                default:
                    ALOGE("UNKNOWN HDR capability: %d", static_cast<int32_t>(hdrType));
            }
        }
    
        float minLuminance = args.hdrCapabilities.getDesiredMinLuminance();
        float maxLuminance = args.hdrCapabilities.getDesiredMaxLuminance();
        float maxAverageLuminance = args.hdrCapabilities.getDesiredMaxAverageLuminance();
    
        minLuminance = minLuminance <= 0.0 ? sDefaultMinLumiance : minLuminance;
        maxLuminance = maxLuminance <= 0.0 ? sDefaultMaxLumiance : maxLuminance;
        maxAverageLuminance = maxAverageLuminance <= 0.0 ? sDefaultMaxLumiance : maxAverageLuminance;
        if (args.hasWideColorGamut) {
            // insert HDR10/HLG as we will force client composition for HDR10/HLG
            // layers
            if (!hasHDR10Support()) {
                types.push_back(ui::Hdr::HDR10);
            }
    
            if (!hasHLGSupport()) {
                types.push_back(ui::Hdr::HLG);
            }
        }
    
        mHdrCapabilities = HdrCapabilities(types, maxLuminance, maxAverageLuminance, minLuminance);
    }
    

    注意这个args参数,从哪里来的,在SurfaceFlinger添加DisplayDevice时,给的参数!

    sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal(
            const wp<IBinder>& displayToken, const std::optional<DisplayId>& displayId,
            const DisplayDeviceState& state, const sp<compositionengine::DisplaySurface>& dispSurface,
            const sp<IGraphicBufferProducer>& producer) {
        DisplayDeviceCreationArgs creationArgs(this, displayToken, displayId);
        creationArgs.sequenceId = state.sequenceId;
        creationArgs.isVirtual = state.isVirtual();
        creationArgs.isSecure = state.isSecure;
        creationArgs.displaySurface = dispSurface;
        creationArgs.hasWideColorGamut = false;
        creationArgs.supportedPerFrameMetadata = 0;
    
        const bool isInternalDisplay = displayId && displayId == getInternalDisplayIdLocked();
        creationArgs.isPrimary = isInternalDisplay;
    
        if (useColorManagement && displayId) {
            std::vector<ColorMode> modes = getHwComposer().getColorModes(*displayId);
            for (ColorMode colorMode : modes) {
                if (isWideColorMode(colorMode)) {
                    creationArgs.hasWideColorGamut = true;
                }
    
                std::vector<RenderIntent> renderIntents =
                        getHwComposer().getRenderIntents(*displayId, colorMode);
                creationArgs.hwcColorModes.emplace(colorMode, renderIntents);
            }
        }
    
        if (displayId) {
            getHwComposer().getHdrCapabilities(*displayId, &creationArgs.hdrCapabilities);
            creationArgs.supportedPerFrameMetadata =
                    getHwComposer().getSupportedPerFrameMetadata(*displayId);
        }
    
        auto nativeWindowSurface = getFactory().createNativeWindowSurface(producer);
        auto nativeWindow = nativeWindowSurface->getNativeWindow();
        creationArgs.nativeWindow = nativeWindow;
    
        // Make sure that composition can never be stalled by a virtual display
        // consumer that isn't processing buffers fast enough. We have to do this
        // here, in case the display is composed entirely by HWC.
        if (state.isVirtual()) {
            nativeWindow->setSwapInterval(nativeWindow.get(), 0);
        }
    
        creationArgs.displayInstallOrientation =
                isInternalDisplay ? primaryDisplayOrientation : DisplayState::eOrientationDefault;
    
        // virtual displays are always considered enabled
        creationArgs.initialPowerMode = state.isVirtual() ? HWC_POWER_MODE_NORMAL : HWC_POWER_MODE_OFF;
    
        sp<DisplayDevice> display = getFactory().createDisplayDevice(std::move(creationArgs));
    
        if (maxFrameBufferAcquiredBuffers >= 3) {
            nativeWindowSurface->preallocateBuffers();
        }
    
        ColorMode defaultColorMode = ColorMode::NATIVE;
        Dataspace defaultDataSpace = Dataspace::UNKNOWN;
        if (display->hasWideColorGamut()) {
            defaultColorMode = ColorMode::SRGB;
            defaultDataSpace = Dataspace::V0_SRGB;
        }
        display->getCompositionDisplay()->setColorMode(defaultColorMode, defaultDataSpace,
                                                       RenderIntent::COLORIMETRIC);
        if (!state.isVirtual()) {
            LOG_ALWAYS_FATAL_IF(!displayId);
            display->setActiveConfig(getHwComposer().getActiveConfigIndex(*displayId));
        }
    
        display->setLayerStack(state.layerStack);
        display->setProjection(state.orientation, state.viewport, state.frame);
        display->setDisplayName(state.displayName);
    
        return display;
    }
    

    ColorModes是在populateColorModes函数中添加的:

    void DisplayColorProfile::populateColorModes(
            const DisplayColorProfileCreationArgs::HwcColorModes& hwcColorModes) {
        if (!hasWideColorGamut()) {
            return;
        }
    
        // collect all known SDR render intents
        std::unordered_set<RenderIntent> sdrRenderIntents(sSdrRenderIntents.begin(),
                                                          sSdrRenderIntents.end());
        auto iter = hwcColorModes.find(ColorMode::SRGB);
        if (iter != hwcColorModes.end()) {
            for (auto intent : iter->second) {
                sdrRenderIntents.insert(intent);
            }
        }
    
        // add all known SDR combinations
        for (auto intent : sdrRenderIntents) {
            for (auto mode : sSdrColorModes) {
                addColorMode(hwcColorModes, mode, intent);
            }
        }
    
        // collect all known HDR render intents
        std::unordered_set<RenderIntent> hdrRenderIntents(sHdrRenderIntents.begin(),
                                                          sHdrRenderIntents.end());
        iter = hwcColorModes.find(ColorMode::BT2100_PQ);
        if (iter != hwcColorModes.end()) {
            for (auto intent : iter->second) {
                hdrRenderIntents.insert(intent);
            }
        }
    
        // add all known HDR combinations
        for (auto intent : hdrRenderIntents) {
            for (auto mode : sHdrColorModes) {
                addColorMode(hwcColorModes, mode, intent);
            }
        }
    }
    

    从这个函数来看,目前主要针对SDR和HDR,系统默认支持的RenderIntent如下:

    // ordered list of known SDR render intents
    const std::array<RenderIntent, 2> sSdrRenderIntents = {
            RenderIntent::ENHANCE,
            RenderIntent::COLORIMETRIC,
    };
    
    // ordered list of known HDR render intents
    const std::array<RenderIntent, 2> sHdrRenderIntents = {
            RenderIntent::TONE_MAP_ENHANCE,
            RenderIntent::TONE_MAP_COLORIMETRIC,
    };
    

    这块贴的代码有点多,是为了保存完整性,很多细节可以先不看!我们先关注一下,上层设置下来的参数,到哪里去了。!这里AddColorMode时,根据dataspace和renderintent生成key,再将hwc支持的对应的hwcDataspace, hwcColorMode, hwcIntent保存下来。

    回到getBestColorMode函数,上层给下的intent,和dataspace。

        static ColorModeKey getColorModeKey(ui::Dataspace dataspace, ui::RenderIntent intent) {
            return (static_cast<uint64_t>(dataspace) << 32) | static_cast<uint32_t>(intent);
        }
    

    根据key,去选取hwcDataspace, hwcColorMode, hwcIntent。所以这里的流程梳理下:

    • 屏幕初始化时,获取屏幕支持的hwcDataspace, hwcColorMode, hwcIntent,转换为SurfaceFlinger中的RenderIntenthedataspace的key来保存
    • 上层设置的DisplayColorSetting转换为对应的RenderIntent
    • 根据显示内容,确定最合适的besedataspace
    • 根据RenderIntent和besedataspace,选取对应的hwcDataspace, hwcColorMode, hwcIntent。

    小结

    我们这里主要讲了上层的颜色管理,接下来看看HAL和底层驱动的具体实现!

    相关文章

      网友评论

        本文标题:Android Q 色彩(颜色)模式解析(一)

        本文链接:https://www.haomeiwen.com/subject/lzczoqtx.html