Loading core/java/android/view/ViewRootImpl.java +32 −18 Original line number Diff line number Diff line Loading @@ -2071,13 +2071,25 @@ public final class ViewRootImpl implements ViewParent, */ @VisibleForTesting public @ForceDarkType.ForceDarkTypeDef int determineForceDarkType() { TypedArray a = mContext.obtainStyledAttributes(R.styleable.Theme); try { if (forceInvertColor()) { // Force invert ignores all developer opt-outs. // We also ignore dark theme, since the app developer can override the user's preference // for dark mode in configuration.uiMode. Instead, we assume that both force invert and // the system's dark theme are enabled. if (getUiModeManager().getForceInvertState() == UiModeManager.FORCE_INVERT_TYPE_DARK) { // We also ignore dark theme, since the app developer can override the user's // preference for dark mode in configuration.uiMode. Instead, we assume that both // force invert and the system's dark theme are enabled. if (getUiModeManager().getForceInvertState() == UiModeManager.FORCE_INVERT_TYPE_DARK) { final boolean isLightTheme = a.getBoolean(R.styleable.Theme_isLightTheme, false); // TODO: b/372558459 - Also check the background ColorDrawable color lightness // TODO: b/368725782 - Use hwui color area detection instead of / in // addition to these heuristics. if (isLightTheme) { return ForceDarkType.FORCE_INVERT_COLOR_DARK; } else { return ForceDarkType.NONE; } } } Loading @@ -2085,12 +2097,14 @@ public final class ViewRootImpl implements ViewParent, if (useAutoDark) { boolean forceDarkAllowedDefault = SystemProperties.getBoolean(ThreadedRenderer.DEBUG_FORCE_DARK, false); TypedArray a = mContext.obtainStyledAttributes(R.styleable.Theme); useAutoDark = a.getBoolean(R.styleable.Theme_isLightTheme, true) && a.getBoolean(R.styleable.Theme_forceDarkAllowed, forceDarkAllowedDefault); a.recycle(); && a.getBoolean(R.styleable.Theme_forceDarkAllowed, forceDarkAllowedDefault); } return useAutoDark ? ForceDarkType.FORCE_DARK : ForceDarkType.NONE; } finally { a.recycle(); } } private void updateForceDarkMode() { Loading libs/hwui/CanvasTransform.cpp +30 −14 Original line number Diff line number Diff line Loading @@ -55,12 +55,20 @@ SkColor makeDark(SkColor color) { } } SkColor invert(SkColor color) { Lab lab = sRGBToLab(color); lab.L = 100 - lab.L; return LabToSRGB(lab, SkColorGetA(color)); } SkColor transformColor(ColorTransform transform, SkColor color) { switch (transform) { case ColorTransform::Light: return makeLight(color); case ColorTransform::Dark: return makeDark(color); case ColorTransform::Invert: return invert(color); default: return color; } Loading @@ -80,19 +88,6 @@ SkColor transformColorInverse(ColorTransform transform, SkColor color) { static void applyColorTransform(ColorTransform transform, SkPaint& paint) { if (transform == ColorTransform::None) return; if (transform == ColorTransform::Invert) { auto filter = SkHighContrastFilter::Make( {/* grayscale= */ false, SkHighContrastConfig::InvertStyle::kInvertLightness, /* contrast= */ 0.0f}); if (paint.getColorFilter()) { paint.setColorFilter(SkColorFilters::Compose(filter, paint.refColorFilter())); } else { paint.setColorFilter(filter); } return; } SkColor newColor = transformColor(transform, paint.getColor()); paint.setColor(newColor); Loading @@ -112,6 +107,22 @@ static void applyColorTransform(ColorTransform transform, SkPaint& paint) { paint.setShader(SkGradientShader::MakeLinear( info.fPoints, info.fColors, info.fColorOffsets, info.fColorCount, info.fTileMode, info.fGradientFlags, nullptr)); } else { if (transform == ColorTransform::Invert) { // Since we're trying to invert every thing around this draw call, we invert // the color of the draw call if we don't know what it is. auto filter = SkHighContrastFilter::Make( {/* grayscale= */ false, SkHighContrastConfig::InvertStyle::kInvertLightness, /* contrast= */ 0.0f}); if (paint.getColorFilter()) { paint.setColorFilter(SkColorFilters::Compose(filter, paint.refColorFilter())); } else { paint.setColorFilter(filter); } return; } } } Loading Loading @@ -150,8 +161,13 @@ bool transformPaint(ColorTransform transform, SkPaint* paint) { } bool transformPaint(ColorTransform transform, SkPaint* paint, BitmapPalette palette) { palette = filterPalette(paint, palette); bool shouldInvert = false; if (transform == ColorTransform::Invert && palette != BitmapPalette::Colorful) { // When the transform is Invert we invert any image that is not deemed "colorful", // regardless of calculated image brightness. shouldInvert = true; } palette = filterPalette(paint, palette); if (palette == BitmapPalette::Light && transform == ColorTransform::Dark) { shouldInvert = true; } Loading libs/hwui/RenderNode.cpp +17 −16 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ #include "DamageAccumulator.h" #include "Debug.h" #include "FeatureFlags.h" #include "Properties.h" #include "TreeInfo.h" #include "VectorDrawable.h" Loading Loading @@ -398,26 +399,32 @@ void RenderNode::syncDisplayList(TreeObserver& observer, TreeInfo* info) { deleteDisplayList(observer, info); mDisplayList = std::move(mStagingDisplayList); if (mDisplayList) { WebViewSyncData syncData{.applyForceDark = shouldEnableForceDark(info)}; WebViewSyncData syncData{.applyForceDark = shouldEnableForceDark(info) || (info && isForceInvertDark(*info))}; mDisplayList.syncContents(syncData); handleForceDark(info); } } // Return true if the tree should use the force invert feature that inverts // the entire tree to darken it. inline bool RenderNode::isForceInvertDark(TreeInfo& info) { return CC_UNLIKELY( info.forceDarkType == android::uirenderer::ForceDarkType::FORCE_INVERT_COLOR_DARK); return CC_UNLIKELY(info.forceDarkType == android::uirenderer::ForceDarkType::FORCE_INVERT_COLOR_DARK); } // Return true if the tree should use the force dark feature that selectively // darkens light nodes on the tree. inline bool RenderNode::shouldEnableForceDark(TreeInfo* info) { return CC_UNLIKELY( info && (!info->disableForceDark || isForceInvertDark(*info))); return CC_UNLIKELY(info && !info->disableForceDark); } void RenderNode::handleForceDark(android::uirenderer::TreeInfo *info) { void RenderNode::handleForceDark(TreeInfo *info) { if (CC_UNLIKELY(view_accessibility_flags::force_invert_color() && info && isForceInvertDark(*info))) { mDisplayList.applyColorTransform(ColorTransform::Invert); return; } if (!shouldEnableForceDark(info)) { return; } Loading @@ -427,14 +434,8 @@ void RenderNode::handleForceDark(android::uirenderer::TreeInfo *info) { children.push_back(node); }); if (mDisplayList.hasText()) { if (isForceInvertDark(*info) && mDisplayList.hasFill()) { // Handle a special case for custom views that draw both text and background in the // same RenderNode, which would otherwise be altered to white-on-white text. usage = UsageHint::Container; } else { usage = UsageHint::Foreground; } } if (usage == UsageHint::Unknown) { if (children.size() > 1) { usage = UsageHint::Background; Loading libs/hwui/hwui/Bitmap.cpp +11 −2 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ #include "Bitmap.h" #include <android-base/file.h> #include "FeatureFlags.h" #include "HardwareBitmapUploader.h" #include "Properties.h" #ifdef __ANDROID__ // Layoutlib does not support render thread Loading Loading @@ -547,9 +549,16 @@ BitmapPalette Bitmap::computePalette(const SkImageInfo& info, const void* addr, } ALOGV("samples = %d, hue [min = %f, max = %f, avg = %f]; saturation [min = %f, max = %f, avg = " "%f]", "%f] %d x %d", sampledCount, hue.min(), hue.max(), hue.average(), saturation.min(), saturation.max(), saturation.average()); saturation.average(), info.width(), info.height()); if (CC_UNLIKELY(view_accessibility_flags::force_invert_color())) { if (saturation.delta() > 0.1f || (hue.delta() > 20 && saturation.average() > 0.2f && value.average() < 0.9f)) { return BitmapPalette::Colorful; } } if (hue.delta() <= 20 && saturation.delta() <= .1f) { if (value.average() >= .5f) { Loading libs/hwui/hwui/Bitmap.h +1 −0 Original line number Diff line number Diff line Loading @@ -49,6 +49,7 @@ enum class BitmapPalette { Unknown, Light, Dark, Colorful, }; namespace uirenderer { Loading Loading
core/java/android/view/ViewRootImpl.java +32 −18 Original line number Diff line number Diff line Loading @@ -2071,13 +2071,25 @@ public final class ViewRootImpl implements ViewParent, */ @VisibleForTesting public @ForceDarkType.ForceDarkTypeDef int determineForceDarkType() { TypedArray a = mContext.obtainStyledAttributes(R.styleable.Theme); try { if (forceInvertColor()) { // Force invert ignores all developer opt-outs. // We also ignore dark theme, since the app developer can override the user's preference // for dark mode in configuration.uiMode. Instead, we assume that both force invert and // the system's dark theme are enabled. if (getUiModeManager().getForceInvertState() == UiModeManager.FORCE_INVERT_TYPE_DARK) { // We also ignore dark theme, since the app developer can override the user's // preference for dark mode in configuration.uiMode. Instead, we assume that both // force invert and the system's dark theme are enabled. if (getUiModeManager().getForceInvertState() == UiModeManager.FORCE_INVERT_TYPE_DARK) { final boolean isLightTheme = a.getBoolean(R.styleable.Theme_isLightTheme, false); // TODO: b/372558459 - Also check the background ColorDrawable color lightness // TODO: b/368725782 - Use hwui color area detection instead of / in // addition to these heuristics. if (isLightTheme) { return ForceDarkType.FORCE_INVERT_COLOR_DARK; } else { return ForceDarkType.NONE; } } } Loading @@ -2085,12 +2097,14 @@ public final class ViewRootImpl implements ViewParent, if (useAutoDark) { boolean forceDarkAllowedDefault = SystemProperties.getBoolean(ThreadedRenderer.DEBUG_FORCE_DARK, false); TypedArray a = mContext.obtainStyledAttributes(R.styleable.Theme); useAutoDark = a.getBoolean(R.styleable.Theme_isLightTheme, true) && a.getBoolean(R.styleable.Theme_forceDarkAllowed, forceDarkAllowedDefault); a.recycle(); && a.getBoolean(R.styleable.Theme_forceDarkAllowed, forceDarkAllowedDefault); } return useAutoDark ? ForceDarkType.FORCE_DARK : ForceDarkType.NONE; } finally { a.recycle(); } } private void updateForceDarkMode() { Loading
libs/hwui/CanvasTransform.cpp +30 −14 Original line number Diff line number Diff line Loading @@ -55,12 +55,20 @@ SkColor makeDark(SkColor color) { } } SkColor invert(SkColor color) { Lab lab = sRGBToLab(color); lab.L = 100 - lab.L; return LabToSRGB(lab, SkColorGetA(color)); } SkColor transformColor(ColorTransform transform, SkColor color) { switch (transform) { case ColorTransform::Light: return makeLight(color); case ColorTransform::Dark: return makeDark(color); case ColorTransform::Invert: return invert(color); default: return color; } Loading @@ -80,19 +88,6 @@ SkColor transformColorInverse(ColorTransform transform, SkColor color) { static void applyColorTransform(ColorTransform transform, SkPaint& paint) { if (transform == ColorTransform::None) return; if (transform == ColorTransform::Invert) { auto filter = SkHighContrastFilter::Make( {/* grayscale= */ false, SkHighContrastConfig::InvertStyle::kInvertLightness, /* contrast= */ 0.0f}); if (paint.getColorFilter()) { paint.setColorFilter(SkColorFilters::Compose(filter, paint.refColorFilter())); } else { paint.setColorFilter(filter); } return; } SkColor newColor = transformColor(transform, paint.getColor()); paint.setColor(newColor); Loading @@ -112,6 +107,22 @@ static void applyColorTransform(ColorTransform transform, SkPaint& paint) { paint.setShader(SkGradientShader::MakeLinear( info.fPoints, info.fColors, info.fColorOffsets, info.fColorCount, info.fTileMode, info.fGradientFlags, nullptr)); } else { if (transform == ColorTransform::Invert) { // Since we're trying to invert every thing around this draw call, we invert // the color of the draw call if we don't know what it is. auto filter = SkHighContrastFilter::Make( {/* grayscale= */ false, SkHighContrastConfig::InvertStyle::kInvertLightness, /* contrast= */ 0.0f}); if (paint.getColorFilter()) { paint.setColorFilter(SkColorFilters::Compose(filter, paint.refColorFilter())); } else { paint.setColorFilter(filter); } return; } } } Loading Loading @@ -150,8 +161,13 @@ bool transformPaint(ColorTransform transform, SkPaint* paint) { } bool transformPaint(ColorTransform transform, SkPaint* paint, BitmapPalette palette) { palette = filterPalette(paint, palette); bool shouldInvert = false; if (transform == ColorTransform::Invert && palette != BitmapPalette::Colorful) { // When the transform is Invert we invert any image that is not deemed "colorful", // regardless of calculated image brightness. shouldInvert = true; } palette = filterPalette(paint, palette); if (palette == BitmapPalette::Light && transform == ColorTransform::Dark) { shouldInvert = true; } Loading
libs/hwui/RenderNode.cpp +17 −16 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ #include "DamageAccumulator.h" #include "Debug.h" #include "FeatureFlags.h" #include "Properties.h" #include "TreeInfo.h" #include "VectorDrawable.h" Loading Loading @@ -398,26 +399,32 @@ void RenderNode::syncDisplayList(TreeObserver& observer, TreeInfo* info) { deleteDisplayList(observer, info); mDisplayList = std::move(mStagingDisplayList); if (mDisplayList) { WebViewSyncData syncData{.applyForceDark = shouldEnableForceDark(info)}; WebViewSyncData syncData{.applyForceDark = shouldEnableForceDark(info) || (info && isForceInvertDark(*info))}; mDisplayList.syncContents(syncData); handleForceDark(info); } } // Return true if the tree should use the force invert feature that inverts // the entire tree to darken it. inline bool RenderNode::isForceInvertDark(TreeInfo& info) { return CC_UNLIKELY( info.forceDarkType == android::uirenderer::ForceDarkType::FORCE_INVERT_COLOR_DARK); return CC_UNLIKELY(info.forceDarkType == android::uirenderer::ForceDarkType::FORCE_INVERT_COLOR_DARK); } // Return true if the tree should use the force dark feature that selectively // darkens light nodes on the tree. inline bool RenderNode::shouldEnableForceDark(TreeInfo* info) { return CC_UNLIKELY( info && (!info->disableForceDark || isForceInvertDark(*info))); return CC_UNLIKELY(info && !info->disableForceDark); } void RenderNode::handleForceDark(android::uirenderer::TreeInfo *info) { void RenderNode::handleForceDark(TreeInfo *info) { if (CC_UNLIKELY(view_accessibility_flags::force_invert_color() && info && isForceInvertDark(*info))) { mDisplayList.applyColorTransform(ColorTransform::Invert); return; } if (!shouldEnableForceDark(info)) { return; } Loading @@ -427,14 +434,8 @@ void RenderNode::handleForceDark(android::uirenderer::TreeInfo *info) { children.push_back(node); }); if (mDisplayList.hasText()) { if (isForceInvertDark(*info) && mDisplayList.hasFill()) { // Handle a special case for custom views that draw both text and background in the // same RenderNode, which would otherwise be altered to white-on-white text. usage = UsageHint::Container; } else { usage = UsageHint::Foreground; } } if (usage == UsageHint::Unknown) { if (children.size() > 1) { usage = UsageHint::Background; Loading
libs/hwui/hwui/Bitmap.cpp +11 −2 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ #include "Bitmap.h" #include <android-base/file.h> #include "FeatureFlags.h" #include "HardwareBitmapUploader.h" #include "Properties.h" #ifdef __ANDROID__ // Layoutlib does not support render thread Loading Loading @@ -547,9 +549,16 @@ BitmapPalette Bitmap::computePalette(const SkImageInfo& info, const void* addr, } ALOGV("samples = %d, hue [min = %f, max = %f, avg = %f]; saturation [min = %f, max = %f, avg = " "%f]", "%f] %d x %d", sampledCount, hue.min(), hue.max(), hue.average(), saturation.min(), saturation.max(), saturation.average()); saturation.average(), info.width(), info.height()); if (CC_UNLIKELY(view_accessibility_flags::force_invert_color())) { if (saturation.delta() > 0.1f || (hue.delta() > 20 && saturation.average() > 0.2f && value.average() < 0.9f)) { return BitmapPalette::Colorful; } } if (hue.delta() <= 20 && saturation.delta() <= .1f) { if (value.average() >= .5f) { Loading
libs/hwui/hwui/Bitmap.h +1 −0 Original line number Diff line number Diff line Loading @@ -49,6 +49,7 @@ enum class BitmapPalette { Unknown, Light, Dark, Colorful, }; namespace uirenderer { Loading