diff options
6 files changed, 109 insertions, 201 deletions
diff --git a/core/java/com/android/internal/policy/TransitionAnimation.java b/core/java/com/android/internal/policy/TransitionAnimation.java index 295dc545ef4b..25ac1bd678c6 100644 --- a/core/java/com/android/internal/policy/TransitionAnimation.java +++ b/core/java/com/android/internal/policy/TransitionAnimation.java @@ -41,12 +41,16 @@ import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; +import android.graphics.ColorSpace; import android.graphics.Picture; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.hardware.HardwareBuffer; +import android.media.Image; +import android.media.ImageReader; import android.os.SystemProperties; import android.util.Slog; +import android.view.SurfaceControl; import android.view.WindowManager.LayoutParams; import android.view.WindowManager.TransitionOldType; import android.view.WindowManager.TransitionType; @@ -59,9 +63,11 @@ import android.view.animation.Interpolator; import android.view.animation.PathInterpolator; import android.view.animation.ScaleAnimation; import android.view.animation.TranslateAnimation; +import android.window.ScreenCapture; import com.android.internal.R; +import java.nio.ByteBuffer; import java.util.List; /** @hide */ @@ -1262,4 +1268,90 @@ public class TransitionAnimation { return set; } + + /** Returns whether the hardware buffer passed in is marked as protected. */ + public static boolean hasProtectedContent(HardwareBuffer hardwareBuffer) { + return (hardwareBuffer.getUsage() & HardwareBuffer.USAGE_PROTECTED_CONTENT) + == HardwareBuffer.USAGE_PROTECTED_CONTENT; + } + + /** Returns the luminance in 0~1. */ + public static float getBorderLuma(SurfaceControl surfaceControl, int w, int h) { + final ScreenCapture.ScreenshotHardwareBuffer buffer = + ScreenCapture.captureLayers(surfaceControl, new Rect(0, 0, w, h), 1); + if (buffer != null) { + return getBorderLuma(buffer.getHardwareBuffer(), buffer.getColorSpace()); + } + return 0; + } + + /** Returns the luminance in 0~1. */ + public static float getBorderLuma(HardwareBuffer hwBuffer, ColorSpace colorSpace) { + if (hwBuffer == null) { + return 0; + } + final int format = hwBuffer.getFormat(); + // Only support RGB format in 4 bytes. And protected buffer is not readable. + if (format != HardwareBuffer.RGBA_8888 || hasProtectedContent(hwBuffer)) { + return 0; + } + + final ImageReader ir = ImageReader.newInstance(hwBuffer.getWidth(), hwBuffer.getHeight(), + format, 1 /* maxImages */); + ir.getSurface().attachAndQueueBufferWithColorSpace(hwBuffer, colorSpace); + final Image image = ir.acquireLatestImage(); + if (image == null || image.getPlaneCount() < 1) { + return 0; + } + + final Image.Plane plane = image.getPlanes()[0]; + final ByteBuffer buffer = plane.getBuffer(); + final int width = image.getWidth(); + final int height = image.getHeight(); + final int pixelStride = plane.getPixelStride(); + final int rowStride = plane.getRowStride(); + final int sampling = 10; + final int[] borderLumas = new int[(width + height) * 2 / sampling]; + + // Grab the top and bottom borders. + int i = 0; + for (int x = 0, size = width - sampling; x < size; x += sampling) { + borderLumas[i++] = getPixelLuminance(buffer, x, 0, pixelStride, rowStride); + borderLumas[i++] = getPixelLuminance(buffer, x, height - 1, pixelStride, rowStride); + } + + // Grab the left and right borders. + for (int y = 0, size = height - sampling; y < size; y += sampling) { + borderLumas[i++] = getPixelLuminance(buffer, 0, y, pixelStride, rowStride); + borderLumas[i++] = getPixelLuminance(buffer, width - 1, y, pixelStride, rowStride); + } + + ir.close(); + + // Get "mode" by histogram. + final int[] histogram = new int[256]; + int maxCount = 0; + int mostLuma = 0; + for (int luma : borderLumas) { + final int count = ++histogram[luma]; + if (count > maxCount) { + maxCount = count; + mostLuma = luma; + } + } + return mostLuma / 255f; + } + + /** Returns the luminance of the pixel in 0~255. */ + private static int getPixelLuminance(ByteBuffer buffer, int x, int y, int pixelStride, + int rowStride) { + final int color = buffer.getInt(y * rowStride + x * pixelStride); + // The buffer from ImageReader is always in native order (little-endian), so extract the + // color components in reversed order. + final int r = color & 0xff; + final int g = (color >> 8) & 0xff; + final int b = (color >> 16) & 0xff; + // Approximation of WCAG 2.0 relative luminance. + return ((r * 8) + (g * 22) + (b * 2)) >> 5; + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java index 2b27baeb515a..66d0a2aa409b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java @@ -16,8 +16,6 @@ package com.android.wm.shell.transition; -import static android.hardware.HardwareBuffer.RGBA_8888; -import static android.hardware.HardwareBuffer.USAGE_PROTECTED_CONTENT; import static android.util.RotationUtils.deltaRotation; import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE; import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_JUMPCUT; @@ -37,8 +35,6 @@ import android.graphics.ColorSpace; import android.graphics.Matrix; import android.graphics.Rect; import android.hardware.HardwareBuffer; -import android.media.Image; -import android.media.ImageReader; import android.util.Slog; import android.view.Surface; import android.view.SurfaceControl; @@ -50,12 +46,11 @@ import android.window.ScreenCapture; import android.window.TransitionInfo; import com.android.internal.R; +import com.android.internal.policy.TransitionAnimation; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.TransactionPool; -import java.nio.ByteBuffer; import java.util.ArrayList; -import java.util.Arrays; /** * This class handles the rotation animation when the device is rotated. @@ -173,7 +168,7 @@ class ScreenRotationAnimation { t.setBuffer(mScreenshotLayer, hardwareBuffer); t.show(mScreenshotLayer); if (!isCustomRotate()) { - mStartLuma = getMedianBorderLuma(hardwareBuffer, colorSpace); + mStartLuma = TransitionAnimation.getBorderLuma(hardwareBuffer, colorSpace); } } @@ -404,93 +399,6 @@ class ScreenRotationAnimation { mTransactionPool.release(t); } - /** - * Converts the provided {@link HardwareBuffer} and converts it to a bitmap to then sample the - * luminance at the borders of the bitmap - * @return the average luminance of all the pixels at the borders of the bitmap - */ - private static float getMedianBorderLuma(HardwareBuffer hardwareBuffer, ColorSpace colorSpace) { - // Cannot read content from buffer with protected usage. - if (hardwareBuffer == null || hardwareBuffer.getFormat() != RGBA_8888 - || hasProtectedContent(hardwareBuffer)) { - return 0; - } - - ImageReader ir = ImageReader.newInstance(hardwareBuffer.getWidth(), - hardwareBuffer.getHeight(), hardwareBuffer.getFormat(), 1); - ir.getSurface().attachAndQueueBufferWithColorSpace(hardwareBuffer, colorSpace); - Image image = ir.acquireLatestImage(); - if (image == null || image.getPlanes().length == 0) { - return 0; - } - - Image.Plane plane = image.getPlanes()[0]; - ByteBuffer buffer = plane.getBuffer(); - int width = image.getWidth(); - int height = image.getHeight(); - int pixelStride = plane.getPixelStride(); - int rowStride = plane.getRowStride(); - float[] borderLumas = new float[2 * width + 2 * height]; - - // Grab the top and bottom borders - int l = 0; - for (int x = 0; x < width; x++) { - borderLumas[l++] = getPixelLuminance(buffer, x, 0, pixelStride, rowStride); - borderLumas[l++] = getPixelLuminance(buffer, x, height - 1, pixelStride, rowStride); - } - - // Grab the left and right borders - for (int y = 0; y < height; y++) { - borderLumas[l++] = getPixelLuminance(buffer, 0, y, pixelStride, rowStride); - borderLumas[l++] = getPixelLuminance(buffer, width - 1, y, pixelStride, rowStride); - } - - // Cleanup - ir.close(); - - // 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]; - } - - /** - * @return whether the hardwareBuffer passed in is marked as protected. - */ - private static boolean hasProtectedContent(HardwareBuffer hardwareBuffer) { - return (hardwareBuffer.getUsage() & USAGE_PROTECTED_CONTENT) == USAGE_PROTECTED_CONTENT; - } - - private static float getPixelLuminance(ByteBuffer buffer, int x, int y, - int pixelStride, int rowStride) { - int offset = y * rowStride + x * pixelStride; - int pixel = 0; - pixel |= (buffer.get(offset) & 0xff) << 16; // R - pixel |= (buffer.get(offset + 1) & 0xff) << 8; // G - pixel |= (buffer.get(offset + 2) & 0xff); // B - pixel |= (buffer.get(offset + 3) & 0xff) << 24; // A - return Color.valueOf(pixel).luminance(); - } - - /** - * Gets the average border luma by taking a screenshot of the {@param surfaceControl}. - * @see #getMedianBorderLuma(HardwareBuffer, ColorSpace) - */ - private static float getLumaOfSurfaceControl(Rect bounds, SurfaceControl surfaceControl) { - if (surfaceControl == null) { - return 0; - } - - Rect crop = new Rect(0, 0, bounds.width(), bounds.height()); - ScreenCapture.ScreenshotHardwareBuffer buffer = - ScreenCapture.captureLayers(surfaceControl, crop, 1); - if (buffer == null) { - return 0; - } - - return getMedianBorderLuma(buffer.getHardwareBuffer(), buffer.getColorSpace()); - } - private static void applyColor(int startColor, int endColor, float[] rgbFloat, float fraction, SurfaceControl surface, SurfaceControl.Transaction t) { final int color = (Integer) ArgbEvaluator.getInstance().evaluate(fraction, startColor, diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java index 55055390b0ff..449e77fca399 100644 --- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java +++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java @@ -55,6 +55,7 @@ import android.view.animation.Transformation; import android.window.ScreenCapture; import com.android.internal.R; +import com.android.internal.policy.TransitionAnimation; import com.android.internal.protolog.common.ProtoLog; import com.android.server.display.DisplayControl; import com.android.server.wm.SurfaceAnimator.AnimationType; @@ -246,7 +247,7 @@ class ScreenRotationAnimation { HardwareBuffer hardwareBuffer = screenshotBuffer.getHardwareBuffer(); Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "ScreenRotationAnimation#getMedianBorderLuma"); - mStartLuma = RotationAnimationUtils.getMedianBorderLuma(hardwareBuffer, + mStartLuma = TransitionAnimation.getBorderLuma(hardwareBuffer, screenshotBuffer.getColorSpace()); Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); @@ -489,8 +490,8 @@ class ScreenRotationAnimation { return false; } if (!mStarted) { - mEndLuma = RotationAnimationUtils.getLumaOfSurfaceControl(mDisplayContent.getDisplay(), - mDisplayContent.getWindowingLayer()); + mEndLuma = TransitionAnimation.getBorderLuma(mDisplayContent.getWindowingLayer(), + finalWidth, finalHeight); startAnimation(t, maxAnimationDuration, animationScale, finalWidth, finalHeight, exitAnim, enterAnim); } diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index 4459d45f60a8..b2c8b7ab98d7 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -83,11 +83,11 @@ import android.window.TransitionInfo; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.graphics.ColorUtils; +import com.android.internal.policy.TransitionAnimation; import com.android.internal.protolog.ProtoLogGroup; import com.android.internal.protolog.common.ProtoLog; import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.inputmethod.InputMethodManagerInternal; -import com.android.server.wm.utils.RotationAnimationUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -2190,7 +2190,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe changeInfo.mSnapshot = snapshotSurface; if (isDisplayRotation) { // This isn't cheap, so only do it for display rotations. - changeInfo.mSnapshotLuma = RotationAnimationUtils.getMedianBorderLuma( + changeInfo.mSnapshotLuma = TransitionAnimation.getBorderLuma( screenshotBuffer.getHardwareBuffer(), screenshotBuffer.getColorSpace()); } SurfaceControl.Transaction t = wc.mWmService.mTransactionFactory.get(); 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 b93b8d866a00..c11a6d02eb18 100644 --- a/services/core/java/com/android/server/wm/utils/RotationAnimationUtils.java +++ b/services/core/java/com/android/server/wm/utils/RotationAnimationUtils.java @@ -16,24 +16,11 @@ package com.android.server.wm.utils; -import static android.hardware.HardwareBuffer.RGBA_8888; import static android.hardware.HardwareBuffer.USAGE_PROTECTED_CONTENT; -import android.graphics.Color; -import android.graphics.ColorSpace; import android.graphics.Matrix; -import android.graphics.Point; -import android.graphics.Rect; import android.hardware.HardwareBuffer; -import android.media.Image; -import android.media.ImageReader; -import android.view.Display; import android.view.Surface; -import android.view.SurfaceControl; -import android.window.ScreenCapture; - -import java.nio.ByteBuffer; -import java.util.Arrays; /** Helper functions for the {@link com.android.server.wm.ScreenRotationAnimation} class*/ @@ -46,89 +33,6 @@ public class RotationAnimationUtils { return (hardwareBuffer.getUsage() & USAGE_PROTECTED_CONTENT) == USAGE_PROTECTED_CONTENT; } - /** - * Converts the provided {@link HardwareBuffer} and converts it to a bitmap to then sample the - * luminance at the borders of the bitmap - * @return the average luminance of all the pixels at the borders of the bitmap - */ - public static float getMedianBorderLuma(HardwareBuffer hardwareBuffer, ColorSpace colorSpace) { - // Cannot read content from buffer with protected usage. - if (hardwareBuffer == null || hardwareBuffer.getFormat() != RGBA_8888 - || hasProtectedContent(hardwareBuffer)) { - return 0; - } - - ImageReader ir = ImageReader.newInstance(hardwareBuffer.getWidth(), - hardwareBuffer.getHeight(), hardwareBuffer.getFormat(), 1); - ir.getSurface().attachAndQueueBufferWithColorSpace(hardwareBuffer, colorSpace); - Image image = ir.acquireLatestImage(); - if (image == null || image.getPlanes().length == 0) { - return 0; - } - - Image.Plane plane = image.getPlanes()[0]; - ByteBuffer buffer = plane.getBuffer(); - int width = image.getWidth(); - int height = image.getHeight(); - int pixelStride = plane.getPixelStride(); - int rowStride = plane.getRowStride(); - float[] borderLumas = new float[2 * width + 2 * height]; - - // Grab the top and bottom borders - int l = 0; - for (int x = 0; x < width; x++) { - borderLumas[l++] = getPixelLuminance(buffer, x, 0, pixelStride, rowStride); - borderLumas[l++] = getPixelLuminance(buffer, x, height - 1, pixelStride, rowStride); - } - - // Grab the left and right borders - for (int y = 0; y < height; y++) { - borderLumas[l++] = getPixelLuminance(buffer, 0, y, pixelStride, rowStride); - borderLumas[l++] = getPixelLuminance(buffer, width - 1, y, pixelStride, rowStride); - } - - // Cleanup - ir.close(); - - // 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]; - } - - private static float getPixelLuminance(ByteBuffer buffer, int x, int y, - int pixelStride, int rowStride) { - int offset = y * rowStride + x * pixelStride; - int pixel = 0; - pixel |= (buffer.get(offset) & 0xff) << 16; // R - pixel |= (buffer.get(offset + 1) & 0xff) << 8; // G - pixel |= (buffer.get(offset + 2) & 0xff); // B - pixel |= (buffer.get(offset + 3) & 0xff) << 24; // A - return Color.valueOf(pixel).luminance(); - } - - /** - * Gets the average border luma by taking a screenshot of the {@param surfaceControl}. - * @see #getMedianBorderLuma(HardwareBuffer, ColorSpace) - */ - public static float getLumaOfSurfaceControl(Display display, SurfaceControl surfaceControl) { - if (surfaceControl == null) { - return 0; - } - - Point size = new Point(); - display.getSize(size); - Rect crop = new Rect(0, 0, size.x, size.y); - ScreenCapture.ScreenshotHardwareBuffer buffer = - ScreenCapture.captureLayers(surfaceControl, crop, 1); - if (buffer == null) { - return 0; - } - - return RotationAnimationUtils.getMedianBorderLuma(buffer.getHardwareBuffer(), - buffer.getColorSpace()); - } - public static void createRotationMatrix(int rotation, int width, int height, Matrix outMatrix) { switch (rotation) { case Surface.ROTATION_0: 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 fc3962bd0b23..cd4d65d7dab1 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 @@ -26,8 +26,10 @@ import android.graphics.ColorSpace; import android.graphics.Matrix; import android.graphics.PointF; import android.hardware.HardwareBuffer; -import android.view.Surface; import android.platform.test.annotations.Presubmit; +import android.view.Surface; + +import com.android.internal.policy.TransitionAnimation; import org.junit.Before; import org.junit.Test; @@ -52,7 +54,8 @@ public class RotationAnimationUtilsTest { public void blackLuma() { Bitmap swBitmap = createBitmap(0); HardwareBuffer hb = swBitmapToHardwareBuffer(swBitmap); - float borderLuma = RotationAnimationUtils.getMedianBorderLuma(hb, mColorSpace); + float borderLuma = TransitionAnimation.getBorderLuma(hb, mColorSpace); + assertEquals(0, borderLuma, 0); } @@ -60,7 +63,7 @@ public class RotationAnimationUtilsTest { public void whiteLuma() { Bitmap swBitmap = createBitmap(1); HardwareBuffer hb = swBitmapToHardwareBuffer(swBitmap); - float borderLuma = RotationAnimationUtils.getMedianBorderLuma(hb, mColorSpace); + float borderLuma = TransitionAnimation.getBorderLuma(hb, mColorSpace); assertEquals(1, borderLuma, 0); } @@ -68,7 +71,7 @@ public class RotationAnimationUtilsTest { public void unevenBitmapDimens() { Bitmap swBitmap = createBitmap(1, BITMAP_WIDTH + 1, BITMAP_HEIGHT + 1); HardwareBuffer hb = swBitmapToHardwareBuffer(swBitmap); - float borderLuma = RotationAnimationUtils.getMedianBorderLuma(hb, mColorSpace); + float borderLuma = TransitionAnimation.getBorderLuma(hb, mColorSpace); assertEquals(1, borderLuma, 0); } @@ -77,7 +80,7 @@ public class RotationAnimationUtilsTest { Bitmap swBitmap = createBitmap(1); setBorderLuma(swBitmap, 0); HardwareBuffer hb = swBitmapToHardwareBuffer(swBitmap); - float borderLuma = RotationAnimationUtils.getMedianBorderLuma(hb, mColorSpace); + float borderLuma = TransitionAnimation.getBorderLuma(hb, mColorSpace); assertEquals(0, borderLuma, 0); } @@ -86,7 +89,7 @@ public class RotationAnimationUtilsTest { Bitmap swBitmap = createBitmap(0); setBorderLuma(swBitmap, 1); HardwareBuffer hb = swBitmapToHardwareBuffer(swBitmap); - float borderLuma = RotationAnimationUtils.getMedianBorderLuma(hb, mColorSpace); + float borderLuma = TransitionAnimation.getBorderLuma(hb, mColorSpace); assertEquals(1, borderLuma, 0); } |