diff options
9 files changed, 322 insertions, 15 deletions
diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java index 3b05b47eb542..a7748f4fae98 100644 --- a/services/core/java/com/android/server/display/DisplayDevice.java +++ b/services/core/java/com/android/server/display/DisplayDevice.java @@ -44,6 +44,15 @@ import java.io.PrintWriter; * </p> */ abstract class DisplayDevice { + /** + * Maximum acceptable anisotropy for the output image. + * + * Necessary to avoid unnecessary scaling when pixels are almost square, as they are non ideal + * anyway. For external displays, we expect an anisotropy of about 2% even if the pixels + * are, in fact, square due to the imprecision of the display's actual size (parsed from edid + * and rounded to the nearest cm). + */ + static final float MAX_ANISOTROPY = 1.025f; private static final String TAG = "DisplayDevice"; private static final Display.Mode EMPTY_DISPLAY_MODE = new Display.Mode.Builder().build(); @@ -69,13 +78,21 @@ abstract class DisplayDevice { // Do not use for any other purpose. DisplayDeviceInfo mDebugLastLoggedDeviceInfo; - public DisplayDevice(DisplayAdapter displayAdapter, IBinder displayToken, String uniqueId, + private final boolean mIsAnisotropyCorrectionEnabled; + + DisplayDevice(DisplayAdapter displayAdapter, IBinder displayToken, String uniqueId, Context context) { + this(displayAdapter, displayToken, uniqueId, context, false); + } + + DisplayDevice(DisplayAdapter displayAdapter, IBinder displayToken, String uniqueId, + Context context, boolean isAnisotropyCorrectionEnabled) { mDisplayAdapter = displayAdapter; mDisplayToken = displayToken; mUniqueId = uniqueId; mDisplayDeviceConfig = null; mContext = context; + mIsAnisotropyCorrectionEnabled = isAnisotropyCorrectionEnabled; } /** @@ -143,8 +160,17 @@ abstract class DisplayDevice { DisplayDeviceInfo displayDeviceInfo = getDisplayDeviceInfoLocked(); final boolean isRotated = mCurrentOrientation == ROTATION_90 || mCurrentOrientation == ROTATION_270; - return isRotated ? new Point(displayDeviceInfo.height, displayDeviceInfo.width) - : new Point(displayDeviceInfo.width, displayDeviceInfo.height); + var width = displayDeviceInfo.width; + var height = displayDeviceInfo.height; + if (mIsAnisotropyCorrectionEnabled && displayDeviceInfo.yDpi > 0 + && displayDeviceInfo.xDpi > 0) { + if (displayDeviceInfo.xDpi > displayDeviceInfo.yDpi * MAX_ANISOTROPY) { + height = (int) (height * displayDeviceInfo.xDpi / displayDeviceInfo.yDpi + 0.5); + } else if (displayDeviceInfo.xDpi * MAX_ANISOTROPY < displayDeviceInfo.yDpi) { + width = (int) (width * displayDeviceInfo.yDpi / displayDeviceInfo.xDpi + 0.5); + } + } + return isRotated ? new Point(height, width) : new Point(width, height); } /** diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index 88c24e0a7eff..b2fd9edf61fe 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -257,7 +257,8 @@ final class LocalDisplayAdapter extends DisplayAdapter { SurfaceControl.DynamicDisplayInfo dynamicInfo, SurfaceControl.DesiredDisplayModeSpecs modeSpecs, boolean isFirstDisplay) { super(LocalDisplayAdapter.this, displayToken, UNIQUE_ID_PREFIX + physicalDisplayId, - getContext()); + getContext(), + getFeatureFlags().isPixelAnisotropyCorrectionInLogicalDisplayEnabled()); mPhysicalDisplayId = physicalDisplayId; mIsFirstDisplay = isFirstDisplay; updateDisplayPropertiesLocked(staticDisplayInfo, dynamicInfo, modeSpecs); diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java index db636d619bd3..5eaaf3504e85 100644 --- a/services/core/java/com/android/server/display/LogicalDisplay.java +++ b/services/core/java/com/android/server/display/LogicalDisplay.java @@ -35,7 +35,6 @@ import android.view.SurfaceControl; import com.android.server.display.layout.Layout; import com.android.server.display.mode.DisplayModeDirector; -import com.android.server.wm.utils.DisplayInfoOverrides; import com.android.server.wm.utils.InsetUtils; import java.io.PrintWriter; @@ -204,7 +203,28 @@ final class LogicalDisplay { private SparseArray<SurfaceControl.RefreshRateRange> mThermalRefreshRateThrottling = new SparseArray<>(); + /** + * If the aspect ratio of the resolution of the display does not match the physical aspect + * ratio of the display, then without this feature enabled, picture would appear stretched to + * the user. This is because applications assume that they are rendered on square pixels + * (meaning density of pixels in x and y directions are equal). This would result into circles + * appearing as ellipses to the user. + * To compensate for non-square (anisotropic) pixels, if this feature is enabled: + * 1. LogicalDisplay will add more pixels for the applications to render on, as if the pixels + * were square and occupied the full display. + * 2. SurfaceFlinger will squeeze this taller/wider surface into the available number of + * physical pixels in the current display resolution. + * 3. If a setting on the display itself is set to "fill the entire display panel" then the + * display will stretch the pixels to fill the display fully. + */ + private final boolean mIsAnisotropyCorrectionEnabled; + LogicalDisplay(int displayId, int layerStack, DisplayDevice primaryDisplayDevice) { + this(displayId, layerStack, primaryDisplayDevice, false); + } + + LogicalDisplay(int displayId, int layerStack, DisplayDevice primaryDisplayDevice, + boolean isAnisotropyCorrectionEnabled) { mDisplayId = displayId; mLayerStack = layerStack; mPrimaryDisplayDevice = primaryDisplayDevice; @@ -215,6 +235,7 @@ final class LogicalDisplay { mThermalBrightnessThrottlingDataId = DisplayDeviceConfig.DEFAULT_ID; mPowerThrottlingDataId = DisplayDeviceConfig.DEFAULT_ID; mBaseDisplayInfo.thermalBrightnessThrottlingDataId = mThermalBrightnessThrottlingDataId; + mIsAnisotropyCorrectionEnabled = isAnisotropyCorrectionEnabled; } public void setDevicePositionLocked(int position) { @@ -453,6 +474,14 @@ final class LogicalDisplay { int maskedWidth = deviceInfo.width - maskingInsets.left - maskingInsets.right; int maskedHeight = deviceInfo.height - maskingInsets.top - maskingInsets.bottom; + if (mIsAnisotropyCorrectionEnabled && deviceInfo.xDpi > 0 && deviceInfo.yDpi > 0) { + if (deviceInfo.xDpi > deviceInfo.yDpi * DisplayDevice.MAX_ANISOTROPY) { + maskedHeight = (int) (maskedHeight * deviceInfo.xDpi / deviceInfo.yDpi + 0.5); + } else if (deviceInfo.xDpi * DisplayDevice.MAX_ANISOTROPY < deviceInfo.yDpi) { + maskedWidth = (int) (maskedWidth * deviceInfo.yDpi / deviceInfo.xDpi + 0.5); + } + } + mBaseDisplayInfo.type = deviceInfo.type; mBaseDisplayInfo.address = deviceInfo.address; mBaseDisplayInfo.deviceProductInfo = deviceInfo.deviceProductInfo; @@ -666,6 +695,31 @@ final class LogicalDisplay { physWidth -= maskingInsets.left + maskingInsets.right; physHeight -= maskingInsets.top + maskingInsets.bottom; + var displayLogicalWidth = displayInfo.logicalWidth; + var displayLogicalHeight = displayInfo.logicalHeight; + + if (mIsAnisotropyCorrectionEnabled && displayDeviceInfo.xDpi > 0 + && displayDeviceInfo.yDpi > 0) { + if (displayDeviceInfo.xDpi > displayDeviceInfo.yDpi * DisplayDevice.MAX_ANISOTROPY) { + var scalingFactor = displayDeviceInfo.yDpi / displayDeviceInfo.xDpi; + if (rotated) { + displayLogicalWidth = (int) ((float) displayLogicalWidth * scalingFactor + 0.5); + } else { + displayLogicalHeight = (int) ((float) displayLogicalHeight * scalingFactor + + 0.5); + } + } else if (displayDeviceInfo.xDpi * DisplayDevice.MAX_ANISOTROPY + < displayDeviceInfo.yDpi) { + var scalingFactor = displayDeviceInfo.xDpi / displayDeviceInfo.yDpi; + if (rotated) { + displayLogicalHeight = (int) ((float) displayLogicalHeight * scalingFactor + + 0.5); + } else { + displayLogicalWidth = (int) ((float) displayLogicalWidth * scalingFactor + 0.5); + } + } + } + // Determine whether the width or height is more constrained to be scaled. // physWidth / displayInfo.logicalWidth => letter box // or physHeight / displayInfo.logicalHeight => pillar box @@ -675,16 +729,16 @@ final class LogicalDisplay { // comparing them. int displayRectWidth, displayRectHeight; if ((displayInfo.flags & Display.FLAG_SCALING_DISABLED) != 0 || mDisplayScalingDisabled) { - displayRectWidth = displayInfo.logicalWidth; - displayRectHeight = displayInfo.logicalHeight; - } else if (physWidth * displayInfo.logicalHeight - < physHeight * displayInfo.logicalWidth) { + displayRectWidth = displayLogicalWidth; + displayRectHeight = displayLogicalHeight; + } else if (physWidth * displayLogicalHeight + < physHeight * displayLogicalWidth) { // Letter box. displayRectWidth = physWidth; - displayRectHeight = displayInfo.logicalHeight * physWidth / displayInfo.logicalWidth; + displayRectHeight = displayLogicalHeight * physWidth / displayLogicalWidth; } else { // Pillar box. - displayRectWidth = displayInfo.logicalWidth * physHeight / displayInfo.logicalHeight; + displayRectWidth = displayLogicalWidth * physHeight / displayLogicalHeight; displayRectHeight = physHeight; } int displayRectTop = (physHeight - displayRectHeight) / 2; diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java index 2e8de31f2af1..d543310f77fa 100644 --- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java +++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java @@ -1149,7 +1149,8 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { */ private LogicalDisplay createNewLogicalDisplayLocked(DisplayDevice device, int displayId) { final int layerStack = assignLayerStackLocked(displayId); - final LogicalDisplay display = new LogicalDisplay(displayId, layerStack, device); + final LogicalDisplay display = new LogicalDisplay(displayId, layerStack, device, + mFlags.isPixelAnisotropyCorrectionInLogicalDisplayEnabled()); display.updateLocked(mDisplayDeviceRepo); final DisplayInfo info = display.getDisplayInfoLocked(); diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java index 516d4b1d4125..f3810288b5e6 100644 --- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java +++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java @@ -121,6 +121,11 @@ public class DisplayManagerFlags { Flags::refreshRateVotingTelemetry ); + private final FlagState mPixelAnisotropyCorrectionEnabled = new FlagState( + Flags.FLAG_ENABLE_PIXEL_ANISOTROPY_CORRECTION, + Flags::enablePixelAnisotropyCorrection + ); + private final FlagState mSensorBasedBrightnessThrottling = new FlagState( Flags.FLAG_SENSOR_BASED_BRIGHTNESS_THROTTLING, Flags::sensorBasedBrightnessThrottling @@ -252,6 +257,10 @@ public class DisplayManagerFlags { return mRefreshRateVotingTelemetry.isEnabled(); } + public boolean isPixelAnisotropyCorrectionInLogicalDisplayEnabled() { + return mPixelAnisotropyCorrectionEnabled.isEnabled(); + } + public boolean isSensorBasedBrightnessThrottlingEnabled() { return mSensorBasedBrightnessThrottling.isEnabled(); } @@ -279,6 +288,7 @@ public class DisplayManagerFlags { pw.println(" " + mAutoBrightnessModesFlagState); pw.println(" " + mFastHdrTransitions); pw.println(" " + mRefreshRateVotingTelemetry); + pw.println(" " + mPixelAnisotropyCorrectionEnabled); pw.println(" " + mSensorBasedBrightnessThrottling); } diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig index 63ab3a95822f..244b8c833015 100644 --- a/services/core/java/com/android/server/display/feature/display_flags.aconfig +++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig @@ -186,6 +186,14 @@ flag { } flag { + name: "enable_pixel_anisotropy_correction" + namespace: "display_manager" + description: "Feature flag for enabling display anisotropy correction through LogicalDisplay upscaling" + bug: "317363416" + is_fixed_read_only: true +} + +flag { name: "sensor_based_brightness_throttling" namespace: "display_manager" description: "Feature flag for enabling brightness throttling using sensor from config." diff --git a/services/core/java/com/android/server/wm/ContentRecorder.java b/services/core/java/com/android/server/wm/ContentRecorder.java index a914c0712319..b616d24cfebb 100644 --- a/services/core/java/com/android/server/wm/ContentRecorder.java +++ b/services/core/java/com/android/server/wm/ContentRecorder.java @@ -107,7 +107,9 @@ final class ContentRecorder implements WindowContainerListener { ContentRecorder(@NonNull DisplayContent displayContent) { this(displayContent, new RemoteMediaProjectionManagerWrapper(displayContent.mDisplayId), - new DisplayManagerFlags().isConnectedDisplayManagementEnabled()); + new DisplayManagerFlags().isConnectedDisplayManagementEnabled() + && !new DisplayManagerFlags() + .isPixelAnisotropyCorrectionInLogicalDisplayEnabled()); } @VisibleForTesting diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceTest.java index dc6abf1981c0..1c71abc984df 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceTest.java @@ -52,7 +52,9 @@ public class DisplayDeviceTest { private static final int WIDTH = 500; private static final int HEIGHT = 900; private static final Point PORTRAIT_SIZE = new Point(WIDTH, HEIGHT); + private static final Point PORTRAIT_DOUBLE_WIDTH = new Point(2 * WIDTH, HEIGHT); private static final Point LANDSCAPE_SIZE = new Point(HEIGHT, WIDTH); + private static final Point LANDSCAPE_DOUBLE_HEIGHT = new Point(HEIGHT, 2 * WIDTH); @Mock private SurfaceControl.Transaction mMockTransaction; @@ -69,6 +71,16 @@ public class DisplayDeviceTest { } @Test + public void testGetDisplaySurfaceDefaultSizeLocked_notRotated_anisotropyCorrection() { + mDisplayDeviceInfo.xDpi = 0.5f; + mDisplayDeviceInfo.yDpi = 1.0f; + DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo, + mMockDisplayAdapter, /*isAnisotropyCorrectionEnabled=*/ true); + assertThat(displayDevice.getDisplaySurfaceDefaultSizeLocked()).isEqualTo( + PORTRAIT_DOUBLE_WIDTH); + } + + @Test public void testGetDisplaySurfaceDefaultSizeLocked_notRotated() { DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo, mMockDisplayAdapter); @@ -84,6 +96,17 @@ public class DisplayDeviceTest { } @Test + public void testGetDisplaySurfaceDefaultSizeLocked_rotation90_anisotropyCorrection() { + mDisplayDeviceInfo.xDpi = 0.5f; + mDisplayDeviceInfo.yDpi = 1.0f; + DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo, + mMockDisplayAdapter, /*isAnisotropyCorrectionEnabled=*/ true); + displayDevice.setProjectionLocked(mMockTransaction, ROTATION_90, new Rect(), new Rect()); + assertThat(displayDevice.getDisplaySurfaceDefaultSizeLocked()).isEqualTo( + LANDSCAPE_DOUBLE_HEIGHT); + } + + @Test public void testGetDisplaySurfaceDefaultSizeLocked_rotation90() { DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo, mMockDisplayAdapter); @@ -111,8 +134,14 @@ public class DisplayDeviceTest { private final DisplayDeviceInfo mDisplayDeviceInfo; FakeDisplayDevice(DisplayDeviceInfo displayDeviceInfo, DisplayAdapter displayAdapter) { + this(displayDeviceInfo, displayAdapter, /*isAnisotropyCorrectionEnabled=*/ false); + } + + FakeDisplayDevice(DisplayDeviceInfo displayDeviceInfo, DisplayAdapter displayAdapter, + boolean isAnisotropyCorrectionEnabled) { super(displayAdapter, /* displayToken= */ null, /* uniqueId= */ "", - InstrumentationRegistry.getInstrumentation().getContext()); + InstrumentationRegistry.getInstrumentation().getContext(), + isAnisotropyCorrectionEnabled); mDisplayDeviceInfo = displayDeviceInfo; } diff --git a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayTest.java b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayTest.java index 1c43418f9276..549f0d74b67b 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayTest.java @@ -106,8 +106,184 @@ public class LogicalDisplayTest { } @Test + public void testLetterbox() { + mLogicalDisplay = new LogicalDisplay(DISPLAY_ID, LAYER_STACK, mDisplayDevice, + /*isAnisotropyCorrectionEnabled=*/ false); + mDisplayDeviceInfo.xDpi = 0.5f; + mDisplayDeviceInfo.yDpi = 1.0f; + + mLogicalDisplay.updateLocked(mDeviceRepo); + var originalDisplayInfo = mLogicalDisplay.getDisplayInfoLocked(); + assertEquals(DISPLAY_WIDTH, originalDisplayInfo.logicalWidth); + assertEquals(DISPLAY_HEIGHT, originalDisplayInfo.logicalHeight); + + SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class); + mLogicalDisplay.configureDisplayLocked(t, mDisplayDevice, false); + assertEquals(new Point(0, 0), mLogicalDisplay.getDisplayPosition()); + + /* + * Content is too wide, should become letterboxed + * ______DISPLAY_WIDTH________ + * | | + * |________________________| + * | | + * | CONTENT | + * | | + * |________________________| + * | | + * |________________________| + */ + // Make a wide application content, by reducing its height. + DisplayInfo displayInfo = new DisplayInfo(); + displayInfo.logicalWidth = DISPLAY_WIDTH; + displayInfo.logicalHeight = DISPLAY_HEIGHT / 2; + mLogicalDisplay.setDisplayInfoOverrideFromWindowManagerLocked(displayInfo); + + mLogicalDisplay.configureDisplayLocked(t, mDisplayDevice, false); + assertEquals(new Point(0, DISPLAY_HEIGHT / 4), mLogicalDisplay.getDisplayPosition()); + } + + @Test + public void testNoLetterbox_anisotropyCorrection() { + mLogicalDisplay = new LogicalDisplay(DISPLAY_ID, LAYER_STACK, mDisplayDevice, + /*isAnisotropyCorrectionEnabled=*/ true); + + // In case of Anisotropy of pixels, then the content should be rescaled so it would adjust + // to using the whole screen. This is because display will rescale it back to fill the + // screen (in case the display menu setting is set to stretch the pixels across the display) + mDisplayDeviceInfo.xDpi = 0.5f; + mDisplayDeviceInfo.yDpi = 1.0f; + + mLogicalDisplay.updateLocked(mDeviceRepo); + var originalDisplayInfo = mLogicalDisplay.getDisplayInfoLocked(); + // Content width re-scaled + assertEquals(DISPLAY_WIDTH * 2, originalDisplayInfo.logicalWidth); + assertEquals(DISPLAY_HEIGHT, originalDisplayInfo.logicalHeight); + + SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class); + mLogicalDisplay.configureDisplayLocked(t, mDisplayDevice, false); + + // Applications need to think that they are shown on a display with square pixels. + // as applications can be displayed on multiple displays simultaneously (mirrored). + // Content is too wide, should have become letterboxed - but it won't because of anisotropy + // correction + assertEquals(new Point(0, 0), mLogicalDisplay.getDisplayPosition()); + } + + @Test + public void testLetterbox_anisotropyCorrectionYDpi() { + mLogicalDisplay = new LogicalDisplay(DISPLAY_ID, LAYER_STACK, mDisplayDevice, + /*isAnisotropyCorrectionEnabled=*/ true); + + DisplayInfo displayInfo = new DisplayInfo(); + displayInfo.logicalWidth = DISPLAY_WIDTH; + displayInfo.logicalHeight = DISPLAY_HEIGHT / 2; + mDisplayDeviceInfo.xDpi = 1.0f; + mDisplayDeviceInfo.yDpi = 0.5f; + mLogicalDisplay.setDisplayInfoOverrideFromWindowManagerLocked(displayInfo); + mLogicalDisplay.updateLocked(mDeviceRepo); + + SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class); + mLogicalDisplay.configureDisplayLocked(t, mDisplayDevice, false); + + assertEquals(new Point(0, 75), mLogicalDisplay.getDisplayPosition()); + } + + @Test + public void testPillarbox() { + mLogicalDisplay = new LogicalDisplay(DISPLAY_ID, LAYER_STACK, mDisplayDevice, + /*isAnisotropyCorrectionEnabled=*/ false); + mDisplayDeviceInfo.xDpi = 0.5f; + mDisplayDeviceInfo.yDpi = 1.0f; + + DisplayInfo displayInfo = new DisplayInfo(); + displayInfo.rotation = Surface.ROTATION_90; + displayInfo.logicalWidth = DISPLAY_WIDTH; + displayInfo.logicalHeight = DISPLAY_HEIGHT; + mDisplayDeviceInfo.flags = DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT; + mLogicalDisplay.setDisplayInfoOverrideFromWindowManagerLocked(displayInfo); + mLogicalDisplay.updateLocked(mDeviceRepo); + + var updatedDisplayInfo = mLogicalDisplay.getDisplayInfoLocked(); + assertEquals(Surface.ROTATION_90, updatedDisplayInfo.rotation); + assertEquals(DISPLAY_WIDTH, updatedDisplayInfo.logicalWidth); + assertEquals(DISPLAY_HEIGHT, updatedDisplayInfo.logicalHeight); + + /* + * Content is too tall, should become pillarboxed + * ______DISPLAY_WIDTH________ + * | | | | + * | | | | + * | | | | + * | | CONTENT | | + * | | | | + * | | | | + * |____|________________|____| + */ + + SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class); + mLogicalDisplay.configureDisplayLocked(t, mDisplayDevice, false); + + assertEquals(new Point(75, 0), mLogicalDisplay.getDisplayPosition()); + } + + @Test + public void testPillarbox_anisotropyCorrection() { + mLogicalDisplay = new LogicalDisplay(DISPLAY_ID, LAYER_STACK, mDisplayDevice, + /*isAnisotropyCorrectionEnabled=*/ true); + + DisplayInfo displayInfo = new DisplayInfo(); + displayInfo.logicalWidth = DISPLAY_WIDTH; + displayInfo.logicalHeight = DISPLAY_HEIGHT; + displayInfo.rotation = Surface.ROTATION_90; + mDisplayDeviceInfo.flags = DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT; + // In case of Anisotropy of pixels, then the content should be rescaled so it would adjust + // to using the whole screen. This is because display will rescale it back to fill the + // screen (in case the display menu setting is set to stretch the pixels across the display) + mDisplayDeviceInfo.xDpi = 0.5f; + mDisplayDeviceInfo.yDpi = 1.0f; + mLogicalDisplay.setDisplayInfoOverrideFromWindowManagerLocked(displayInfo); + mLogicalDisplay.updateLocked(mDeviceRepo); + + SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class); + mLogicalDisplay.configureDisplayLocked(t, mDisplayDevice, false); + + // Applications need to think that they are shown on a display with square pixels. + // as applications can be displayed on multiple displays simultaneously (mirrored). + // Content is a bit wider than in #testPillarbox, due to content added stretching + assertEquals(new Point(50, 0), mLogicalDisplay.getDisplayPosition()); + } + + @Test + public void testNoPillarbox_anisotropyCorrectionYDpi() { + mLogicalDisplay = new LogicalDisplay(DISPLAY_ID, LAYER_STACK, mDisplayDevice, + /*isAnisotropyCorrectionEnabled=*/ true); + + // In case of Anisotropy of pixels, then the content should be rescaled so it would adjust + // to using the whole screen. This is because display will rescale it back to fill the + // screen (in case the display menu setting is set to stretch the pixels across the display) + mDisplayDeviceInfo.xDpi = 1.0f; + mDisplayDeviceInfo.yDpi = 0.5f; + + mLogicalDisplay.updateLocked(mDeviceRepo); + var originalDisplayInfo = mLogicalDisplay.getDisplayInfoLocked(); + // Content width re-scaled + assertEquals(DISPLAY_WIDTH, originalDisplayInfo.logicalWidth); + assertEquals(DISPLAY_HEIGHT * 2, originalDisplayInfo.logicalHeight); + + SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class); + mLogicalDisplay.configureDisplayLocked(t, mDisplayDevice, false); + + // Applications need to think that they are shown on a display with square pixels. + // as applications can be displayed on multiple displays simultaneously (mirrored). + // Content is too tall, should have occupy the whole screen - but it won't because of + // anisotropy correction + assertEquals(new Point(0, 0), mLogicalDisplay.getDisplayPosition()); + } + + @Test public void testGetDisplayPosition() { - Point expectedPosition = new Point(); + Point expectedPosition = new Point(0, 0); SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class); mLogicalDisplay.configureDisplayLocked(t, mDisplayDevice, false); |