Android Q 在系统设置中提供了可设置的色彩模式,当然这功能很多厂商早就有了~,落后归落后,我们还是看看Android是怎么实现的!
Android Q提供了4种色彩模式:
下面我们就结合实际代码,看看具体的实现和流程!
为了实现色彩模式的切换,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种色彩模式,主要通过两个参数的调节来实现的
private static final float COLOR_SATURATION_NATURAL = 1.0f;
private static final float COLOR_SATURATION_BOOSTED = 1.1f;
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_SATURATION
和 SURFACE_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中,饱和度用饱和因子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(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 has_wide_color_display() {
return GetProp>("ro.surface_flinger.has_wide_color_display");
}
颜色管理,是针对屏幕的,不是对单个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版本!
我们来看看几个比较常用的:
* 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,
};
* 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
};
* 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.
*/
};
// Pick the ColorMode / Dataspace for the display device.
void SurfaceFlinger::pickColorMode(const sp& 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(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& 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函数:
property_get("persist.sys.sf.color_mode", value, "0");
mForceColorMode = static_cast(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(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 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(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 SurfaceFlinger::setupNewDisplayDeviceInternal(
const wp& displayToken, const std::optional& displayId,
const DisplayDeviceState& state, const sp& dispSurface,
const sp& 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 modes = getHwComposer().getColorModes(*displayId);
for (ColorMode colorMode : modes) {
if (isWideColorMode(colorMode)) {
creationArgs.hasWideColorGamut = true;
}
std::vector 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 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 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 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 sSdrRenderIntents = {
RenderIntent::ENHANCE,
RenderIntent::COLORIMETRIC,
};
// ordered list of known HDR render intents
const std::array 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(dataspace) << 32) | static_cast(intent);
}
根据key,去选取hwcDataspace, hwcColorMode, hwcIntent。所以这里的流程梳理下:
我们这里主要讲了上层的颜色管理,接下来看看HAL和底层驱动的具体实现!