Auto-dark mode prototype
Experimental force_dark prototype mode. Enabled
by setting debug.hwui.force_dark to true.
Test: verified nothing changes without prop being set
Change-Id: Ib02f3f1a9c591cab1f312b827451f04c782c2f41
diff --git a/core/java/android/view/RenderNode.java b/core/java/android/view/RenderNode.java
index e10eeb0..e0df59f 100644
--- a/core/java/android/view/RenderNode.java
+++ b/core/java/android/view/RenderNode.java
@@ -16,6 +16,7 @@
package android.view;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Matrix;
@@ -29,6 +30,9 @@
import libcore.util.NativeAllocationRegistry;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* <p>A display list records a series of graphics related operations and can replay
* them later. Display lists are usually built by recording operations on a
@@ -449,6 +453,25 @@
return nSetHasOverlappingRendering(mNativeRenderNode, hasOverlappingRendering);
}
+ /** @hide */
+ @IntDef({USAGE_BACKGROUND})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface UsageHint {}
+
+ /** The default usage hint */
+ public static final int USAGE_UNKNOWN = 0;
+
+ /** Usage is background content */
+ public static final int USAGE_BACKGROUND = 1;
+
+ /**
+ * Provides a hint on what this RenderNode's display list content contains. This hint is used
+ * for automatic content transforms to improve accessibility or similar.
+ */
+ public void setUsageHint(@UsageHint int usageHint) {
+ nSetUsageHint(mNativeRenderNode, usageHint);
+ }
+
/**
* Indicates whether the content of this display list overlaps.
*
@@ -948,6 +971,8 @@
private static native boolean nSetHasOverlappingRendering(long renderNode,
boolean hasOverlappingRendering);
@CriticalNative
+ private static native void nSetUsageHint(long renderNode, int usageHint);
+ @CriticalNative
private static native boolean nSetElevation(long renderNode, float lift);
@CriticalNative
private static native boolean nSetTranslationX(long renderNode, float translationX);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 546ea87..19e95b8 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -20442,6 +20442,7 @@
private RenderNode getDrawableRenderNode(Drawable drawable, RenderNode renderNode) {
if (renderNode == null) {
renderNode = RenderNode.create(drawable.getClass().getName(), this);
+ renderNode.setUsageHint(RenderNode.USAGE_BACKGROUND);
}
final Rect bounds = drawable.getBounds();
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index 22bbc3c..46b19bd 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -222,6 +222,11 @@
RenderNode::GENERIC);
}
+static void android_view_RenderNode_setUsageHint(jlong renderNodePtr, jint usageHint) {
+ RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+ renderNode->setUsageHint(static_cast<UsageHint>(usageHint));
+}
+
static jboolean android_view_RenderNode_setElevation(jlong renderNodePtr, float elevation) {
return SET_AND_DIRTY(setElevation, elevation, RenderNode::Z);
}
@@ -614,6 +619,7 @@
{ "nSetAlpha", "(JF)Z", (void*) android_view_RenderNode_setAlpha },
{ "nSetHasOverlappingRendering", "(JZ)Z",
(void*) android_view_RenderNode_setHasOverlappingRendering },
+ { "nSetUsageHint", "(JI)V", (void*) android_view_RenderNode_setUsageHint },
{ "nSetElevation", "(JF)Z", (void*) android_view_RenderNode_setElevation },
{ "nSetTranslationX", "(JF)Z", (void*) android_view_RenderNode_setTranslationX },
{ "nSetTranslationY", "(JF)Z", (void*) android_view_RenderNode_setTranslationY },
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 0db7799..111094b 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -198,6 +198,7 @@
"AnimatorManager.cpp",
"Caches.cpp",
"CanvasState.cpp",
+ "CanvasTransform.cpp",
"ClipArea.cpp",
"DamageAccumulator.cpp",
"DeferredLayerUpdater.cpp",
diff --git a/libs/hwui/CanvasTransform.cpp b/libs/hwui/CanvasTransform.cpp
new file mode 100644
index 0000000..bac7a4d
--- /dev/null
+++ b/libs/hwui/CanvasTransform.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "CanvasTransform.h"
+#include "Properties.h"
+
+#include <SkColorFilter.h>
+#include <SkPaint.h>
+#include <log/log.h>
+
+namespace android::uirenderer {
+
+static SkColor makeLight(SkColor color) {
+ SkScalar hsv[3];
+ SkColorToHSV(color, hsv);
+ if (hsv[1] > .2f) return color;
+ // hsv[1] *= .85f;
+ // hsv[2] = std::min(1.0f, std::max(hsv[2], 1 - hsv[2]) * 1.3f);
+ hsv[2] = std::max(hsv[2], 1.1f - hsv[2]);
+ return SkHSVToColor(SkColorGetA(color), hsv);
+}
+
+static SkColor makeDark(SkColor color) {
+ SkScalar hsv[3];
+ SkColorToHSV(color, hsv);
+ if (hsv[1] > .2f) return color;
+ // hsv[1] *= .85f;
+ // hsv[2] = std::max(0.0f, std::min(hsv[2], 1 - hsv[2]) * .7f);
+ hsv[2] = std::min(hsv[2], 1.1f - hsv[2]);
+ return SkHSVToColor(SkColorGetA(color), hsv);
+}
+
+static SkColor transformColor(ColorTransform transform, SkColor color) {
+ switch (transform) {
+ case ColorTransform::Light:
+ return makeLight(color);
+ case ColorTransform::Dark:
+ return makeDark(color);
+ default:
+ return color;
+ }
+}
+
+static void applyColorTransform(ColorTransform transform, SkPaint& paint) {
+ if (transform == ColorTransform::None) return;
+
+ SkColor newColor = transformColor(transform, paint.getColor());
+ paint.setColor(newColor);
+
+ if (paint.getColorFilter()) {
+ SkBlendMode mode;
+ SkColor color;
+ // TODO: LRU this or something to avoid spamming new color mode filters
+ if (paint.getColorFilter()->asColorMode(&color, &mode)) {
+ color = transformColor(transform, color);
+ paint.setColorFilter(SkColorFilter::MakeModeFilter(color, mode));
+ }
+ }
+}
+
+class ColorFilterCanvas : public SkPaintFilterCanvas {
+public:
+ ColorFilterCanvas(ColorTransform transform, SkCanvas* canvas)
+ : SkPaintFilterCanvas(canvas), mTransform(transform) {}
+
+ bool onFilter(SkTCopyOnFirstWrite<SkPaint>* paint, Type type) const override {
+ if (*paint) {
+ applyColorTransform(mTransform, *(paint->writable()));
+ }
+ return true;
+ }
+
+private:
+ ColorTransform mTransform;
+};
+
+std::unique_ptr<SkCanvas> makeTransformCanvas(SkCanvas* inCanvas, ColorTransform transform) {
+ switch (transform) {
+ case ColorTransform::Light:
+ return std::make_unique<ColorFilterCanvas>(ColorTransform::Light, inCanvas);
+ case ColorTransform::Dark:
+ return std::make_unique<ColorFilterCanvas>(ColorTransform::Dark, inCanvas);
+ default:
+ return nullptr;
+ }
+}
+
+std::unique_ptr<SkCanvas> makeTransformCanvas(SkCanvas* inCanvas, UsageHint usageHint) {
+ if (Properties::forceDarkMode) {
+ switch (usageHint) {
+ case UsageHint::Unknown:
+ return makeTransformCanvas(inCanvas, ColorTransform::Light);
+ case UsageHint::Background:
+ return makeTransformCanvas(inCanvas, ColorTransform::Dark);
+ }
+ }
+ return nullptr;
+}
+
+}; // namespace android::uirenderer
\ No newline at end of file
diff --git a/libs/hwui/CanvasTransform.h b/libs/hwui/CanvasTransform.h
new file mode 100644
index 0000000..f71fdfa
--- /dev/null
+++ b/libs/hwui/CanvasTransform.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <SkCanvas.h>
+#include <SkPaintFilterCanvas.h>
+
+#include <memory>
+
+namespace android::uirenderer {
+
+enum class UsageHint {
+ Unknown = 0,
+ Background = 1,
+};
+
+enum class ColorTransform {
+ None,
+ Light,
+ Dark,
+};
+
+std::unique_ptr<SkCanvas> makeTransformCanvas(SkCanvas* inCanvas, ColorTransform transform);
+std::unique_ptr<SkCanvas> makeTransformCanvas(SkCanvas* inCanvas, UsageHint usageHint);
+
+} // namespace android::uirenderer;
\ No newline at end of file
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 0338a3a..17bec19 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -60,6 +60,7 @@
bool Properties::filterOutTestOverhead = false;
bool Properties::disableVsync = false;
bool Properties::skpCaptureEnabled = false;
+bool Properties::forceDarkMode = false;
bool Properties::enableRTAnimations = true;
bool Properties::runningInEmulator = false;
@@ -146,6 +147,8 @@
runningInEmulator = property_get_bool(PROPERTY_QEMU_KERNEL, false);
+ forceDarkMode = property_get_bool(PROPERTY_FORCE_DARK, false);
+
return (prevDebugLayersUpdates != debugLayersUpdates) || (prevDebugOverdraw != debugOverdraw) ||
(prevDebugStencilClip != debugStencilClip);
}
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index d640c74..ea017a7 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -190,6 +190,8 @@
*/
#define PROPERTY_QEMU_KERNEL "ro.kernel.qemu"
+#define PROPERTY_FORCE_DARK "debug.hwui.force_dark"
+
///////////////////////////////////////////////////////////////////////////////
// Misc
///////////////////////////////////////////////////////////////////////////////
@@ -263,6 +265,7 @@
static bool disableVsync;
static bool skpCaptureEnabled;
+ static bool forceDarkMode;
// For experimentation b/68769804
ANDROID_API static bool enableRTAnimations;
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index dc962f3..8393288 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -28,6 +28,7 @@
#include <androidfw/ResourceTypes.h>
#include "AnimatorManager.h"
+#include "CanvasTransform.h"
#include "Debug.h"
#include "DisplayList.h"
#include "Matrix.h"
@@ -208,6 +209,14 @@
void output(std::ostream& output, uint32_t level);
+ void setUsageHint(UsageHint usageHint) {
+ mUsageHint = usageHint;
+ }
+
+ UsageHint usageHint() const {
+ return mUsageHint;
+ }
+
private:
void computeOrderingImpl(RenderNodeOp* opState,
std::vector<RenderNodeOp*>* compositedChildrenOfProjectionSurface,
@@ -263,6 +272,8 @@
sp<PositionListener> mPositionListener;
+ UsageHint mUsageHint = UsageHint::Unknown;
+
// METHODS & FIELDS ONLY USED BY THE SKIA RENDERER
public:
/**
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index f0da660..6fb2ee0 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -17,6 +17,7 @@
#include "SkiaRecordingCanvas.h"
#include <SkImagePriv.h>
+#include "CanvasTransform.h"
#include "Layer.h"
#include "LayerDrawable.h"
#include "NinePatchUtils.h"
@@ -44,13 +45,19 @@
}
mDisplayList->attachRecorder(&mRecorder, SkIRect::MakeWH(width, height));
- SkiaCanvas::reset(&mRecorder);
+ SkCanvas* canvas = &mRecorder;
+ mWrappedCanvas = makeTransformCanvas(&mRecorder, renderNode->usageHint());
+ if (mWrappedCanvas) {
+ canvas = mWrappedCanvas.get();
+ }
+ SkiaCanvas::reset(canvas);
}
uirenderer::DisplayList* SkiaRecordingCanvas::finishRecording() {
// close any existing chunks if necessary
insertReorderBarrier(false);
mRecorder.restoreToCount(1);
+ mWrappedCanvas = nullptr;
return mDisplayList.release();
}
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
index 93807a5..b69acbf 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
@@ -78,6 +78,7 @@
private:
SkLiteRecorder mRecorder;
std::unique_ptr<SkiaDisplayList> mDisplayList;
+ std::unique_ptr<SkCanvas> mWrappedCanvas;
StartReorderBarrierDrawable* mCurrentBarrier;
/**