Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3589c2c6 authored by Mihai Popa's avatar Mihai Popa
Browse files

[Magnifier-16] Clamp to surface against distortion

Previously, we were only making sure that the magnified content belongs
to the view the magnifier is attached to. However, when the view was
laid out partially outside the screen, we would pixel copy from outside
the surface the view is attached to. This would lead to the user seeing
a distorted content in the magnifier, in cases when the magnified view
lies outside the screen. This CL addresses this issue, by clamping the
pixel copy coordinates inside the surface we copy the magnifier content
from.

Bug: 72039853
Bug: 63531115
Test: bit CtsWidgetTestCases:android.widget.cts.MagnifierTest
Change-Id: Iddab05c98b615259938e0d3a3320b98b3b13b246
parent 7d0e1f80
Loading
Loading
Loading
Loading
+20 −0
Original line number Diff line number Diff line
@@ -895,6 +895,26 @@ public final class ViewRootImpl implements ViewParent,
        return mWindowAttributes.getTitle();
    }

    /**
     * @return the width of the root view. Note that this will return {@code -1} until the first
     *         layout traversal, when the width is set.
     *
     * @hide
     */
    public int getWidth() {
        return mWidth;
    }

    /**
     * @return the height of the root view. Note that this will return {@code -1} until the first
     *         layout traversal, when the height is set.
     *
     * @hide
     */
    public int getHeight() {
        return mHeight;
    }

    void destroyHardwareResources() {
        if (mAttachInfo.mThreadedRenderer != null) {
            mAttachInfo.mThreadedRenderer.destroyHardwareResources(mView);
+39 −22
Original line number Diff line number Diff line
@@ -18,7 +18,6 @@ package android.widget;

import android.annotation.FloatRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UiThread;
import android.content.Context;
import android.graphics.Bitmap;
@@ -30,9 +29,11 @@ import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.PixelCopy;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewParent;
import android.view.ViewRootImpl;

import com.android.internal.util.Preconditions;

@@ -124,7 +125,8 @@ public final class Magnifier {

        configureCoordinates(xPosInView, yPosInView);

        // Clamp startX value to avoid distorting the rendering of the magnifier content.
        // Clamp the startX value to avoid magnifying content which does not belong to the magnified
        // view. This will not take into account overlapping views.
        // For this, we compute:
        // - zeroScrollXInSurface: this is the start x of mView, where this is not masked by a
        //                         potential scrolling container. For example, if mView is a
@@ -214,11 +216,41 @@ public final class Magnifier {
    }

    private void performPixelCopy(final int startXInSurface, final int startYInSurface) {
        final Surface surface = getValidViewSurface();
        if (surface != null) {
            mPixelCopyRequestRect.set(startXInSurface, startYInSurface,
                    startXInSurface + mBitmap.getWidth(), startYInSurface + mBitmap.getHeight());
        // Get the view surface where the content will be copied from.
        final Surface surface;
        final int surfaceWidth;
        final int surfaceHeight;
        if (mView instanceof SurfaceView) {
            final SurfaceHolder surfaceHolder = ((SurfaceView) mView).getHolder();
            surface = surfaceHolder.getSurface();
            surfaceWidth = surfaceHolder.getSurfaceFrame().right;
            surfaceHeight = surfaceHolder.getSurfaceFrame().bottom;
        } else if (mView.getViewRootImpl() != null) {
            final ViewRootImpl viewRootImpl = mView.getViewRootImpl();
            surface = viewRootImpl.mSurface;
            surfaceWidth = viewRootImpl.getWidth();
            surfaceHeight = viewRootImpl.getHeight();
        } else {
            surface = null;
            surfaceWidth = NONEXISTENT_PREVIOUS_CONFIG_VALUE;
            surfaceHeight = NONEXISTENT_PREVIOUS_CONFIG_VALUE;
        }

        if (surface == null || !surface.isValid()) {
            return;
        }

        // Clamp copy coordinates inside the surface to avoid displaying distorted content.
        final int clampedStartXInSurface = Math.max(0,
                Math.min(startXInSurface, surfaceWidth - mWindowWidth));
        final int clampedStartYInSurface = Math.max(0,
                Math.min(startYInSurface, surfaceHeight - mWindowHeight));

        // Perform the pixel copy.
        mPixelCopyRequestRect.set(clampedStartXInSurface,
                clampedStartYInSurface,
                clampedStartXInSurface + mBitmap.getWidth(),
                clampedStartYInSurface + mBitmap.getHeight());
        PixelCopy.request(surface, mPixelCopyRequestRect, mBitmap,
                result -> {
                    getImageView().invalidate();
@@ -227,21 +259,6 @@ public final class Magnifier {
                },
                mPixelCopyHandler);
    }
    }

    @Nullable
    private Surface getValidViewSurface() {
        final Surface surface;
        if (mView instanceof SurfaceView) {
            surface = ((SurfaceView) mView).getHolder().getSurface();
        } else if (mView.getViewRootImpl() != null) {
            surface = mView.getViewRootImpl().mSurface;
        } else {
            surface = null;
        }

        return (surface != null && surface.isValid()) ? surface : null;
    }

    private ImageView getImageView() {
        return mWindow.getContentView().findViewById(