From c4bbdff6db25c41653b1d3651f4edffa86e96fbc Mon Sep 17 00:00:00 2001 From: Vinit Nayak Date: Sat, 22 Feb 2020 14:42:49 -0800 Subject: [PATCH] Use median instead of mean for sampling border luma Using median of the luma for all border pixels gives a more accurate indication of dominant background color to use during the screen rotation animation than does using the average. fixes: 150056201 Test: Existing tests pass Also can visually see the change in Google Photos app where you have a bright image on the black background. Now background stays as black instead of being gray. Change-Id: I87bc87930aff48f90f271fc1783e70caf61dc7f4 --- .../server/wm/ScreenRotationAnimation.java | 2 +- .../wm/utils/RotationAnimationUtils.java | 28 +++++++++++-------- .../wm/utils/RotationAnimationUtilsTest.java | 26 ++++++++++++----- 3 files changed, 37 insertions(+), 19 deletions(-) diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java index f6cdac5d45652..68975b9d2abd9 100644 --- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java +++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java @@ -205,7 +205,7 @@ class ScreenRotationAnimation { SurfaceControl.ScreenshotGraphicBuffer gb = mService.mDisplayManagerInternal.screenshot(displayId); if (gb != null) { - mStartLuma = RotationAnimationUtils.getAvgBorderLuma(gb.getGraphicBuffer(), + mStartLuma = RotationAnimationUtils.getMedianBorderLuma(gb.getGraphicBuffer(), gb.getColorSpace()); try { surface.attachAndQueueBufferWithColorSpace(gb.getGraphicBuffer(), diff --git a/services/core/java/com/android/server/wm/utils/RotationAnimationUtils.java b/services/core/java/com/android/server/wm/utils/RotationAnimationUtils.java index 0c09c8828682e..d02f79f3ad162 100644 --- a/services/core/java/com/android/server/wm/utils/RotationAnimationUtils.java +++ b/services/core/java/com/android/server/wm/utils/RotationAnimationUtils.java @@ -26,6 +26,8 @@ import android.view.Display; import android.view.Surface; import android.view.SurfaceControl; +import java.util.Arrays; + /** Helper functions for the {@link com.android.server.wm.ScreenRotationAnimation} class*/ public class RotationAnimationUtils { @@ -35,31 +37,35 @@ public class RotationAnimationUtils { * luminance at the borders of the bitmap * @return the average luminance of all the pixels at the borders of the bitmap */ - public static float getAvgBorderLuma(GraphicBuffer graphicBuffer, ColorSpace colorSpace) { + public static float getMedianBorderLuma(GraphicBuffer graphicBuffer, ColorSpace colorSpace) { Bitmap hwBitmap = Bitmap.wrapHardwareBuffer(graphicBuffer, colorSpace); if (hwBitmap == null) { return 0; } Bitmap swaBitmap = hwBitmap.copy(Bitmap.Config.ARGB_8888, false); - float totalLuma = 0; int height = swaBitmap.getHeight(); int width = swaBitmap.getWidth(); + float[] borderLumas = new float[2 * width + 2 * height]; int i; - for (i = 0; i < width; i++) { - totalLuma += swaBitmap.getColor(i, 0).luminance(); - totalLuma += swaBitmap.getColor(i, height - 1).luminance(); + int index = 0; + for (i = 0; i < width; i++, index += 2) { + borderLumas[index] = swaBitmap.getColor(i, 0).luminance(); + borderLumas[index + 1] = swaBitmap.getColor(i, height - 1).luminance(); } - for (i = 0; i < height; i++) { - totalLuma += swaBitmap.getColor(0, i).luminance(); - totalLuma += swaBitmap.getColor(width - 1, i).luminance(); + for (i = 0; i < height; i++, index += 2) { + borderLumas[index] = swaBitmap.getColor(0, i).luminance(); + borderLumas[index + 1] = swaBitmap.getColor(width - 1, i).luminance(); } - return totalLuma / (2 * width + 2 * height); + // Oh, is this too simple and inefficient for you? + // How about implementing a O(n) solution? https://en.wikipedia.org/wiki/Median_of_medians + Arrays.sort(borderLumas); + return borderLumas[borderLumas.length / 2]; } /** * Gets the average border luma by taking a screenshot of the {@param surfaceControl}. - * @see #getAvgBorderLuma(GraphicBuffer, ColorSpace) + * @see #getMedianBorderLuma(GraphicBuffer, ColorSpace) */ public static float getLumaOfSurfaceControl(Display display, SurfaceControl surfaceControl) { if (surfaceControl == null) { @@ -75,7 +81,7 @@ public class RotationAnimationUtils { return 0; } - return RotationAnimationUtils.getAvgBorderLuma(buffer.getGraphicBuffer(), + return RotationAnimationUtils.getMedianBorderLuma(buffer.getGraphicBuffer(), buffer.getColorSpace()); } diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/RotationAnimationUtilsTest.java b/services/tests/wmtests/src/com/android/server/wm/utils/RotationAnimationUtilsTest.java index 9cda084586403..e5497a2313d17 100644 --- a/services/tests/wmtests/src/com/android/server/wm/utils/RotationAnimationUtilsTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/utils/RotationAnimationUtilsTest.java @@ -50,7 +50,7 @@ public class RotationAnimationUtilsTest { public void blackLuma() { Bitmap swBitmap = createBitmap(0); GraphicBuffer gb = swBitmapToGraphicsBuffer(swBitmap); - float borderLuma = RotationAnimationUtils.getAvgBorderLuma(gb, mColorSpace); + float borderLuma = RotationAnimationUtils.getMedianBorderLuma(gb, mColorSpace); assertEquals(0, borderLuma, 0); } @@ -58,7 +58,15 @@ public class RotationAnimationUtilsTest { public void whiteLuma() { Bitmap swBitmap = createBitmap(1); GraphicBuffer gb = swBitmapToGraphicsBuffer(swBitmap); - float borderLuma = RotationAnimationUtils.getAvgBorderLuma(gb, mColorSpace); + float borderLuma = RotationAnimationUtils.getMedianBorderLuma(gb, mColorSpace); + assertEquals(1, borderLuma, 0); + } + + @Test + public void unevenBitmapDimens() { + Bitmap swBitmap = createBitmap(1, BITMAP_WIDTH + 1, BITMAP_HEIGHT + 1); + GraphicBuffer gb = swBitmapToGraphicsBuffer(swBitmap); + float borderLuma = RotationAnimationUtils.getMedianBorderLuma(gb, mColorSpace); assertEquals(1, borderLuma, 0); } @@ -67,7 +75,7 @@ public class RotationAnimationUtilsTest { Bitmap swBitmap = createBitmap(1); setBorderLuma(swBitmap, 0); GraphicBuffer gb = swBitmapToGraphicsBuffer(swBitmap); - float borderLuma = RotationAnimationUtils.getAvgBorderLuma(gb, mColorSpace); + float borderLuma = RotationAnimationUtils.getMedianBorderLuma(gb, mColorSpace); assertEquals(0, borderLuma, 0); } @@ -76,7 +84,7 @@ public class RotationAnimationUtilsTest { Bitmap swBitmap = createBitmap(0); setBorderLuma(swBitmap, 1); GraphicBuffer gb = swBitmapToGraphicsBuffer(swBitmap); - float borderLuma = RotationAnimationUtils.getAvgBorderLuma(gb, mColorSpace); + float borderLuma = RotationAnimationUtils.getMedianBorderLuma(gb, mColorSpace); assertEquals(1, borderLuma, 0); } @@ -123,9 +131,13 @@ public class RotationAnimationUtilsTest { } private Bitmap createBitmap(float luma) { - Bitmap bitmap = Bitmap.createBitmap(BITMAP_WIDTH, BITMAP_HEIGHT, ARGB_8888); - for (int i = 0; i < BITMAP_WIDTH; i++) { - for (int j = 0; j < BITMAP_HEIGHT; j++) { + return createBitmap(luma, BITMAP_WIDTH, BITMAP_HEIGHT); + } + + private Bitmap createBitmap(float luma, int width, int height) { + Bitmap bitmap = Bitmap.createBitmap(width, height, ARGB_8888); + for (int i = 0; i < width; i++) { + for (int j = 0; j < height; j++) { bitmap.setPixel(i, j, Color.argb(1, luma, luma, luma)); } }