From c875ba736f4a569198c6b5aabea8a8bea2b04b75 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Thu, 19 Mar 2020 12:06:46 -0700 Subject: [PATCH] Minor optimization to skip bitmap copy during luma sampling - Attach the hw buffer to an image reader surface and sample the bytes directly Bug: 149318305 Test: Rotate the device and measure the time to get the luma Change-Id: I50512d6741109ee07a309f840c2936afd19af96e --- .../server/wm/ScreenRotationAnimation.java | 6 ++ .../wm/utils/RotationAnimationUtils.java | 61 ++++++++++++++----- 2 files changed, 53 insertions(+), 14 deletions(-) diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java index 68975b9d2abd9..b92ead1a05315 100644 --- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java +++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java @@ -16,6 +16,8 @@ package com.android.server.wm; +import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; + import static com.android.server.wm.AnimationSpecProto.ROTATE; import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION; import static com.android.server.wm.ProtoLogGroup.WM_SHOW_SURFACE_ALLOC; @@ -36,6 +38,7 @@ import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Point; import android.graphics.Rect; +import android.os.Trace; import android.util.Slog; import android.util.proto.ProtoOutputStream; import android.view.Display; @@ -205,8 +208,11 @@ class ScreenRotationAnimation { SurfaceControl.ScreenshotGraphicBuffer gb = mService.mDisplayManagerInternal.screenshot(displayId); if (gb != null) { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, + "ScreenRotationAnimation#getMedianBorderLuma"); mStartLuma = RotationAnimationUtils.getMedianBorderLuma(gb.getGraphicBuffer(), gb.getColorSpace()); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); try { surface.attachAndQueueBufferWithColorSpace(gb.getGraphicBuffer(), gb.getColorSpace()); 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 d02f79f3ad162..9dee7af590330 100644 --- a/services/core/java/com/android/server/wm/utils/RotationAnimationUtils.java +++ b/services/core/java/com/android/server/wm/utils/RotationAnimationUtils.java @@ -16,16 +16,21 @@ package com.android.server.wm.utils; -import android.graphics.Bitmap; +import static android.graphics.PixelFormat.RGBA_8888; + +import android.graphics.Color; import android.graphics.ColorSpace; import android.graphics.GraphicBuffer; import android.graphics.Matrix; import android.graphics.Point; import android.graphics.Rect; +import android.media.Image; +import android.media.ImageReader; import android.view.Display; import android.view.Surface; import android.view.SurfaceControl; +import java.nio.ByteBuffer; import java.util.Arrays; @@ -38,31 +43,59 @@ public class RotationAnimationUtils { * @return the average luminance of all the pixels at the borders of the bitmap */ public static float getMedianBorderLuma(GraphicBuffer graphicBuffer, ColorSpace colorSpace) { - Bitmap hwBitmap = Bitmap.wrapHardwareBuffer(graphicBuffer, colorSpace); - if (hwBitmap == null) { + if (graphicBuffer == null || graphicBuffer.getFormat() != RGBA_8888) { return 0; } - Bitmap swaBitmap = hwBitmap.copy(Bitmap.Config.ARGB_8888, false); - int height = swaBitmap.getHeight(); - int width = swaBitmap.getWidth(); + ImageReader ir = ImageReader.newInstance(graphicBuffer.getWidth(), + graphicBuffer.getHeight(), graphicBuffer.getFormat(), 1); + ir.getSurface().attachAndQueueBufferWithColorSpace(graphicBuffer, 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]; - int i; - 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(); + + // 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); } - for (i = 0; i < height; i++, index += 2) { - borderLumas[index] = swaBitmap.getColor(0, i).luminance(); - borderLumas[index + 1] = swaBitmap.getColor(width - 1, i).luminance(); + + // 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(GraphicBuffer, ColorSpace)