Loading core/java/com/android/internal/policy/TransitionAnimation.java +92 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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 */ Loading Loading @@ -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; } } libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java +2 −94 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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. Loading Loading @@ -173,7 +168,7 @@ class ScreenRotationAnimation { t.setBuffer(mScreenshotLayer, hardwareBuffer); t.show(mScreenshotLayer); if (!isCustomRotate()) { mStartLuma = getMedianBorderLuma(hardwareBuffer, colorSpace); mStartLuma = TransitionAnimation.getBorderLuma(hardwareBuffer, colorSpace); } } Loading Loading @@ -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, Loading services/core/java/com/android/server/wm/ScreenRotationAnimation.java +4 −3 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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); } Loading services/core/java/com/android/server/wm/Transition.java +2 −2 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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(); Loading services/core/java/com/android/server/wm/utils/RotationAnimationUtils.java +0 −96 Original line number Diff line number Diff line Loading @@ -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*/ Loading @@ -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: Loading Loading
core/java/com/android/internal/policy/TransitionAnimation.java +92 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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 */ Loading Loading @@ -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; } }
libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java +2 −94 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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. Loading Loading @@ -173,7 +168,7 @@ class ScreenRotationAnimation { t.setBuffer(mScreenshotLayer, hardwareBuffer); t.show(mScreenshotLayer); if (!isCustomRotate()) { mStartLuma = getMedianBorderLuma(hardwareBuffer, colorSpace); mStartLuma = TransitionAnimation.getBorderLuma(hardwareBuffer, colorSpace); } } Loading Loading @@ -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, Loading
services/core/java/com/android/server/wm/ScreenRotationAnimation.java +4 −3 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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); } Loading
services/core/java/com/android/server/wm/Transition.java +2 −2 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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(); Loading
services/core/java/com/android/server/wm/utils/RotationAnimationUtils.java +0 −96 Original line number Diff line number Diff line Loading @@ -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*/ Loading @@ -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: Loading