Android(安卓)Q 色彩(颜色)模式解析(一)
文章目录
- Android 色彩(颜色)模式解析(一)
- FrameWork色彩模式的定义及实现
- SurfaceFlinger对色彩模式的支持和实现
- 看屏幕是否支持颜色管理
- 屏幕的ColorProfile
- SurfaceFlinger设置ColorMode的流程
- 小结
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_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对色彩模式的支持和实现
通过前面的分析,我们其实只需关心饱和度和显示颜色!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_managementro.surface_flinger.has_HDR_displayro.surface_flinger.has_wide_color_display
我们来看一个属性接口的描述:
* frameworks/native/services/surfaceflinger/sysprop/SurfaceFlingerProperties.syspropprop { 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");}
屏幕的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& 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_PQDataspace 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函数:
- 可以人为的指定dataspace,通过mForceColorMode:
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.cppDisplayColorProfile::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 intentsconst std::array sSdrRenderIntents = { RenderIntent::ENHANCE, RenderIntent::COLORIMETRIC,};// ordered list of known HDR render intentsconst 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。所以这里的流程梳理下:
- 屏幕初始化时,获取屏幕支持的hwcDataspace, hwcColorMode, hwcIntent,转换为SurfaceFlinger中的RenderIntenthedataspace的key来保存
- 上层设置的DisplayColorSetting转换为对应的RenderIntent
- 根据显示内容,确定最合适的besedataspace
- 根据RenderIntent和besedataspace,选取对应的hwcDataspace, hwcColorMode, hwcIntent。
小结
我们这里主要讲了上层的颜色管理,接下来看看HAL和底层驱动的具体实现!
更多相关文章
- android调试模式的操作技巧,调试BUG极快呀
- ANDROID下多种网络模式的联网处理
- Android资源文件Res详解——ShapeDrawable
- Android学习笔记(九)ListView
- Android运行时配置更改
- Android四大组件 - Activity的生命周期和启动模式
- android中数据库创建操作的模式
- 【Android】颜色的透明度
- Android与MVC设计模式