1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.wm.utils; 18 19 import static android.hardware.HardwareBuffer.RGBA_8888; 20 import static android.hardware.HardwareBuffer.USAGE_PROTECTED_CONTENT; 21 22 import android.graphics.Color; 23 import android.graphics.ColorSpace; 24 import android.graphics.Matrix; 25 import android.graphics.Point; 26 import android.graphics.Rect; 27 import android.hardware.HardwareBuffer; 28 import android.media.Image; 29 import android.media.ImageReader; 30 import android.view.Display; 31 import android.view.Surface; 32 import android.view.SurfaceControl; 33 34 import java.nio.ByteBuffer; 35 import java.util.Arrays; 36 37 38 /** Helper functions for the {@link com.android.server.wm.ScreenRotationAnimation} class*/ 39 public class RotationAnimationUtils { 40 41 /** 42 * @return whether the hardwareBuffer passed in is marked as protected. 43 */ hasProtectedContent(HardwareBuffer hardwareBuffer)44 public static boolean hasProtectedContent(HardwareBuffer hardwareBuffer) { 45 return (hardwareBuffer.getUsage() & USAGE_PROTECTED_CONTENT) == USAGE_PROTECTED_CONTENT; 46 } 47 48 /** 49 * Converts the provided {@link HardwareBuffer} and converts it to a bitmap to then sample the 50 * luminance at the borders of the bitmap 51 * @return the average luminance of all the pixels at the borders of the bitmap 52 */ getMedianBorderLuma(HardwareBuffer hardwareBuffer, ColorSpace colorSpace)53 public static float getMedianBorderLuma(HardwareBuffer hardwareBuffer, ColorSpace colorSpace) { 54 // Cannot read content from buffer with protected usage. 55 if (hardwareBuffer == null || hardwareBuffer.getFormat() != RGBA_8888 56 || hasProtectedContent(hardwareBuffer)) { 57 return 0; 58 } 59 60 ImageReader ir = ImageReader.newInstance(hardwareBuffer.getWidth(), 61 hardwareBuffer.getHeight(), hardwareBuffer.getFormat(), 1); 62 ir.getSurface().attachAndQueueBufferWithColorSpace(hardwareBuffer, colorSpace); 63 Image image = ir.acquireLatestImage(); 64 if (image == null || image.getPlanes().length == 0) { 65 return 0; 66 } 67 68 Image.Plane plane = image.getPlanes()[0]; 69 ByteBuffer buffer = plane.getBuffer(); 70 int width = image.getWidth(); 71 int height = image.getHeight(); 72 int pixelStride = plane.getPixelStride(); 73 int rowStride = plane.getRowStride(); 74 float[] borderLumas = new float[2 * width + 2 * height]; 75 76 // Grab the top and bottom borders 77 int l = 0; 78 for (int x = 0; x < width; x++) { 79 borderLumas[l++] = getPixelLuminance(buffer, x, 0, pixelStride, rowStride); 80 borderLumas[l++] = getPixelLuminance(buffer, x, height - 1, pixelStride, rowStride); 81 } 82 83 // Grab the left and right borders 84 for (int y = 0; y < height; y++) { 85 borderLumas[l++] = getPixelLuminance(buffer, 0, y, pixelStride, rowStride); 86 borderLumas[l++] = getPixelLuminance(buffer, width - 1, y, pixelStride, rowStride); 87 } 88 89 // Cleanup 90 ir.close(); 91 92 // Oh, is this too simple and inefficient for you? 93 // How about implementing a O(n) solution? https://en.wikipedia.org/wiki/Median_of_medians 94 Arrays.sort(borderLumas); 95 return borderLumas[borderLumas.length / 2]; 96 } 97 getPixelLuminance(ByteBuffer buffer, int x, int y, int pixelStride, int rowStride)98 private static float getPixelLuminance(ByteBuffer buffer, int x, int y, 99 int pixelStride, int rowStride) { 100 int offset = y * rowStride + x * pixelStride; 101 int pixel = 0; 102 pixel |= (buffer.get(offset) & 0xff) << 16; // R 103 pixel |= (buffer.get(offset + 1) & 0xff) << 8; // G 104 pixel |= (buffer.get(offset + 2) & 0xff); // B 105 pixel |= (buffer.get(offset + 3) & 0xff) << 24; // A 106 return Color.valueOf(pixel).luminance(); 107 } 108 109 /** 110 * Gets the average border luma by taking a screenshot of the {@param surfaceControl}. 111 * @see #getMedianBorderLuma(HardwareBuffer, ColorSpace) 112 */ getLumaOfSurfaceControl(Display display, SurfaceControl surfaceControl)113 public static float getLumaOfSurfaceControl(Display display, SurfaceControl surfaceControl) { 114 if (surfaceControl == null) { 115 return 0; 116 } 117 118 Point size = new Point(); 119 display.getSize(size); 120 Rect crop = new Rect(0, 0, size.x, size.y); 121 SurfaceControl.ScreenshotHardwareBuffer buffer = 122 SurfaceControl.captureLayers(surfaceControl, crop, 1); 123 if (buffer == null) { 124 return 0; 125 } 126 127 return RotationAnimationUtils.getMedianBorderLuma(buffer.getHardwareBuffer(), 128 buffer.getColorSpace()); 129 } 130 createRotationMatrix(int rotation, int width, int height, Matrix outMatrix)131 public static void createRotationMatrix(int rotation, int width, int height, Matrix outMatrix) { 132 switch (rotation) { 133 case Surface.ROTATION_0: 134 outMatrix.reset(); 135 break; 136 case Surface.ROTATION_90: 137 outMatrix.setRotate(90, 0, 0); 138 outMatrix.postTranslate(height, 0); 139 break; 140 case Surface.ROTATION_180: 141 outMatrix.setRotate(180, 0, 0); 142 outMatrix.postTranslate(width, height); 143 break; 144 case Surface.ROTATION_270: 145 outMatrix.setRotate(270, 0, 0); 146 outMatrix.postTranslate(0, width); 147 break; 148 } 149 } 150 } 151