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

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和底層驅動的具體實現!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章