Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d144d16b authored by Daniel Norman's avatar Daniel Norman Committed by Android (Google) Code Review
Browse files

Merge "feat(force invert): force invert the entire light-themed app" into main

parents 912e6ea5 fc89f1c7
Loading
Loading
Loading
Loading
+32 −18
Original line number Diff line number Diff line
@@ -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;
                    }
                }
            }
@@ -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() {
+30 −14
Original line number Diff line number Diff line
@@ -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;
    }
@@ -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);

@@ -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;
            }
        }
    }

@@ -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;
    }
+17 −16
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@

#include "DamageAccumulator.h"
#include "Debug.h"
#include "FeatureFlags.h"
#include "Properties.h"
#include "TreeInfo.h"
#include "VectorDrawable.h"
@@ -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;
    }
@@ -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;
+11 −2
Original line number Diff line number Diff line
@@ -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
@@ -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) {
+1 −0
Original line number Diff line number Diff line
@@ -49,6 +49,7 @@ enum class BitmapPalette {
    Unknown,
    Light,
    Dark,
    Colorful,
};

namespace uirenderer {