Fix fading edge effect from impacting neighboring pixels
Bug: 129117085
Test: skia unit tests and test cases described in the bug
Change-Id: Ieaa7c831dd6298ac0565e6f1837b1c1dbd4545da
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 58429f4..bcd570a 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -22013,23 +22013,27 @@
}
saveCount = canvas.getSaveCount();
+ int topSaveCount = -1;
+ int bottomSaveCount = -1;
+ int leftSaveCount = -1;
+ int rightSaveCount = -1;
int solidColor = getSolidColor();
if (solidColor == 0) {
if (drawTop) {
- canvas.saveUnclippedLayer(left, top, right, top + length);
+ topSaveCount = canvas.saveUnclippedLayer(left, top, right, top + length);
}
if (drawBottom) {
- canvas.saveUnclippedLayer(left, bottom - length, right, bottom);
+ bottomSaveCount = canvas.saveUnclippedLayer(left, bottom - length, right, bottom);
}
if (drawLeft) {
- canvas.saveUnclippedLayer(left, top, left + length, bottom);
+ leftSaveCount = canvas.saveUnclippedLayer(left, top, left + length, bottom);
}
if (drawRight) {
- canvas.saveUnclippedLayer(right - length, top, right, bottom);
+ rightSaveCount = canvas.saveUnclippedLayer(right - length, top, right, bottom);
}
} else {
scrollabilityCache.setFadeColor(solidColor);
@@ -22046,21 +22050,19 @@
final Matrix matrix = scrollabilityCache.matrix;
final Shader fade = scrollabilityCache.shader;
- if (drawTop) {
- matrix.setScale(1, fadeHeight * topFadeStrength);
- matrix.postTranslate(left, top);
+ // must be restored in the reverse order that they were saved
+ if (drawRight) {
+ matrix.setScale(1, fadeHeight * rightFadeStrength);
+ matrix.postRotate(90);
+ matrix.postTranslate(right, top);
fade.setLocalMatrix(matrix);
p.setShader(fade);
- canvas.drawRect(left, top, right, top + length, p);
- }
+ if (solidColor == 0) {
+ canvas.restoreUnclippedLayer(rightSaveCount, p);
- if (drawBottom) {
- matrix.setScale(1, fadeHeight * bottomFadeStrength);
- matrix.postRotate(180);
- matrix.postTranslate(left, bottom);
- fade.setLocalMatrix(matrix);
- p.setShader(fade);
- canvas.drawRect(left, bottom - length, right, bottom, p);
+ } else {
+ canvas.drawRect(right - length, top, right, bottom, p);
+ }
}
if (drawLeft) {
@@ -22069,16 +22071,36 @@
matrix.postTranslate(left, top);
fade.setLocalMatrix(matrix);
p.setShader(fade);
- canvas.drawRect(left, top, left + length, bottom, p);
+ if (solidColor == 0) {
+ canvas.restoreUnclippedLayer(leftSaveCount, p);
+ } else {
+ canvas.drawRect(left, top, left + length, bottom, p);
+ }
}
- if (drawRight) {
- matrix.setScale(1, fadeHeight * rightFadeStrength);
- matrix.postRotate(90);
- matrix.postTranslate(right, top);
+ if (drawBottom) {
+ matrix.setScale(1, fadeHeight * bottomFadeStrength);
+ matrix.postRotate(180);
+ matrix.postTranslate(left, bottom);
fade.setLocalMatrix(matrix);
p.setShader(fade);
- canvas.drawRect(right - length, top, right, bottom, p);
+ if (solidColor == 0) {
+ canvas.restoreUnclippedLayer(bottomSaveCount, p);
+ } else {
+ canvas.drawRect(left, bottom - length, right, bottom, p);
+ }
+ }
+
+ if (drawTop) {
+ matrix.setScale(1, fadeHeight * topFadeStrength);
+ matrix.postTranslate(left, top);
+ fade.setLocalMatrix(matrix);
+ p.setShader(fade);
+ if (solidColor == 0) {
+ canvas.restoreUnclippedLayer(topSaveCount, p);
+ } else {
+ canvas.drawRect(left, top, right, top + length, p);
+ }
}
canvas.restoreToCount(saveCount);
diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp
index 7a8c5c8..ebc6cd7 100644
--- a/core/jni/android_graphics_Canvas.cpp
+++ b/core/jni/android_graphics_Canvas.cpp
@@ -106,6 +106,11 @@
return reinterpret_cast<jint>(get_canvas(canvasHandle)->saveUnclippedLayer(l, t, r, b));
}
+static void restoreUnclippedLayer(jlong canvasHandle, jint saveCount, jlong paintHandle) {
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ get_canvas(canvasHandle)->restoreUnclippedLayer(saveCount, *paint);
+}
+
static bool restore(jlong canvasHandle) {
Canvas* canvas = get_canvas(canvasHandle);
if (canvas->getSaveCount() <= 1) {
@@ -668,6 +673,7 @@
{"nSaveLayer","(JFFFFJI)I", (void*) CanvasJNI::saveLayer},
{"nSaveLayerAlpha","(JFFFFII)I", (void*) CanvasJNI::saveLayerAlpha},
{"nSaveUnclippedLayer","(JIIII)I", (void*) CanvasJNI::saveUnclippedLayer},
+ {"nRestoreUnclippedLayer","(JIJ)V", (void*) CanvasJNI::restoreUnclippedLayer},
{"nGetSaveCount","(J)I", (void*) CanvasJNI::getSaveCount},
{"nRestore","(J)Z", (void*) CanvasJNI::restore},
{"nRestoreToCount","(JI)V", (void*) CanvasJNI::restoreToCount},
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index d4d5ae7..5648b85 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -559,6 +559,16 @@
}
/**
+ * @hide
+ * @param saveCount The save level to restore to.
+ * @param paint This is copied and is applied to the area within the unclipped layer's
+ * bounds (i.e. equivalent to a drawPaint()) before restore() is called.
+ */
+ public void restoreUnclippedLayer(int saveCount, Paint paint) {
+ nRestoreUnclippedLayer(mNativeCanvasWrapper, saveCount, paint.getNativeInstance());
+ }
+
+ /**
* Helper version of saveLayer() that takes 4 values rather than a RectF.
*
* As of API Level API level {@value Build.VERSION_CODES#P} the only valid
@@ -1398,6 +1408,9 @@
@CriticalNative
private static native int nSaveUnclippedLayer(long nativeCanvas, int l, int t, int r, int b);
@CriticalNative
+ private static native void nRestoreUnclippedLayer(long nativeCanvas, int saveCount,
+ long nativePaint);
+ @CriticalNative
private static native boolean nRestore(long canvasHandle);
@CriticalNative
private static native void nRestoreToCount(long canvasHandle, int saveCount);
diff --git a/libs/hwui/DisplayListOps.in b/libs/hwui/DisplayListOps.in
index 14e3a32..2deb565 100644
--- a/libs/hwui/DisplayListOps.in
+++ b/libs/hwui/DisplayListOps.in
@@ -26,7 +26,8 @@
X(ClipRect)
X(ClipRRect)
X(ClipRegion)
-X(DrawPaint)
+X(DrawPaint)
+X(DrawBehind)
X(DrawPath)
X(DrawRect)
X(DrawRegion)
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 8594766..e58fbbe 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -20,6 +20,7 @@
#include "SkAndroidFrameworkUtils.h"
#include "SkCanvas.h"
+#include "SkCanvasPriv.h"
#include "SkData.h"
#include "SkDrawShadowInfo.h"
#include "SkImage.h"
@@ -187,6 +188,12 @@
SkPaint paint;
void draw(SkCanvas* c, const SkMatrix&) const { c->drawPaint(paint); }
};
+struct DrawBehind final : Op {
+ static const auto kType = Type::DrawBehind;
+ DrawBehind(const SkPaint& paint) : paint(paint) {}
+ SkPaint paint;
+ void draw(SkCanvas* c, const SkMatrix&) const { SkCanvasPriv::DrawBehind(c, paint); }
+};
struct DrawPath final : Op {
static const auto kType = Type::DrawPath;
DrawPath(const SkPath& path, const SkPaint& paint) : path(path), paint(paint) {}
@@ -565,6 +572,9 @@
void DisplayListData::drawPaint(const SkPaint& paint) {
this->push<DrawPaint>(0, paint);
}
+void DisplayListData::drawBehind(const SkPaint& paint) {
+ this->push<DrawBehind>(0, paint);
+}
void DisplayListData::drawPath(const SkPath& path, const SkPaint& paint) {
this->push<DrawPath>(0, path, paint);
}
@@ -834,6 +844,9 @@
void RecordingCanvas::onDrawPaint(const SkPaint& paint) {
fDL->drawPaint(paint);
}
+void RecordingCanvas::onDrawBehind(const SkPaint& paint) {
+ fDL->drawBehind(paint);
+}
void RecordingCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
fDL->drawPath(path, paint);
}
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 3a76ca1..7269bca 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -89,6 +89,7 @@
void clipRegion(const SkRegion&, SkClipOp);
void drawPaint(const SkPaint&);
+ void drawBehind(const SkPaint&);
void drawPath(const SkPath&, const SkPaint&);
void drawRect(const SkRect&, const SkPaint&);
void drawRegion(const SkRegion&, const SkPaint&);
@@ -157,6 +158,7 @@
void onClipRegion(const SkRegion&, SkClipOp) override;
void onDrawPaint(const SkPaint&) override;
+ void onDrawBehind(const SkPaint&) override;
void onDrawPath(const SkPath&, const SkPaint&) override;
void onDrawRect(const SkRect&, const SkPaint&) override;
void onDrawRegion(const SkRegion&, const SkPaint&) override;
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 8340699..6ea6af8 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -26,6 +26,7 @@
#include <SkAndroidFrameworkUtils.h>
#include <SkAnimatedImage.h>
+#include <SkCanvasPriv.h>
#include <SkCanvasStateUtils.h>
#include <SkColorFilter.h>
#include <SkDeque.h>
@@ -191,6 +192,18 @@
return SkAndroidFrameworkUtils::SaveBehind(mCanvas, &bounds);
}
+void SkiaCanvas::restoreUnclippedLayer(int restoreCount, const SkPaint& paint) {
+
+ while (mCanvas->getSaveCount() > restoreCount + 1) {
+ this->restore();
+ }
+
+ if (mCanvas->getSaveCount() == restoreCount + 1) {
+ SkCanvasPriv::DrawBehind(mCanvas, *filterPaint(paint));
+ this->restore();
+ }
+}
+
class SkiaCanvas::Clip {
public:
Clip(const SkRect& rect, SkClipOp op, const SkMatrix& m)
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index 3fe2bce..bbe91eb 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -69,6 +69,7 @@
virtual int save(SaveFlags::Flags flags) override;
virtual void restore() override;
virtual void restoreToCount(int saveCount) override;
+ virtual void restoreUnclippedLayer(int saveCount, const SkPaint& paint) override;
virtual int saveLayer(float left, float top, float right, float bottom, const SkPaint* paint,
SaveFlags::Flags flags) override;
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index 11e8579..ac8db21 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -191,6 +191,7 @@
virtual int save(SaveFlags::Flags flags) = 0;
virtual void restore() = 0;
virtual void restoreToCount(int saveCount) = 0;
+ virtual void restoreUnclippedLayer(int saveCount, const SkPaint& paint) = 0;
virtual int saveLayer(float left, float top, float right, float bottom, const SkPaint* paint,
SaveFlags::Flags flags) = 0;