blob: 7fa14418f1a3a10f34ed1585ae4c921cccf1330b [file] [log] [blame]
package android.uirendering.cts.testclasses;
import static org.junit.Assert.assertTrue;
import android.graphics.Color;
import android.graphics.Outline;
import android.graphics.Path;
import android.graphics.Rect;
import android.uirendering.cts.R;
import android.uirendering.cts.bitmapverifiers.AntiAliasingVerifier;
import android.uirendering.cts.bitmapverifiers.BitmapVerifier;
import android.uirendering.cts.bitmapverifiers.RectVerifier;
import android.uirendering.cts.bitmapverifiers.RegionVerifier;
import android.uirendering.cts.testclasses.view.UnclippedBlueView;
import android.uirendering.cts.testinfrastructure.ActivityTestBase;
import android.uirendering.cts.testinfrastructure.ViewInitializer;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
import androidx.test.filters.MediumTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
/**
* This tests view clipping by modifying properties of blue_padded_layout, and validating
* the resulting rect of content.
*
* Since the layout is blue on a white background, this is always done with a RectVerifier.
*/
@MediumTest
@RunWith(AndroidJUnit4.class)
public class ViewClippingTests extends ActivityTestBase {
static final Rect FULL_RECT = new Rect(0, 0, 90, 90);
static final Rect BOUNDS_RECT = new Rect(0, 0, 80, 80);
static final Rect PADDED_RECT = new Rect(15, 16, 63, 62);
static final Rect OUTLINE_RECT = new Rect(1, 2, 78, 79);
static final Rect ANTI_ALIAS_OUTLINE_RECT = new Rect(20, 10, 80, 80);
static final Rect CLIP_BOUNDS_RECT = new Rect(10, 20, 50, 60);
static final Rect CONCAVE_OUTLINE_RECT1 = new Rect(0, 0, 10, 90);
static final Rect CONCAVE_TEST_RECT1 = new Rect(0, 10, 90, 90);
static final Rect CONCAVE_OUTLINE_RECT2 = new Rect(0, 0, 90, 10);
static final Rect CONCAVE_TEST_RECT2 = new Rect(10, 0, 90, 90);
static final ViewInitializer BOUNDS_CLIP_INIT =
rootView -> ((ViewGroup)rootView).setClipChildren(true);
static final ViewInitializer PADDING_CLIP_INIT = rootView -> {
ViewGroup child = (ViewGroup) rootView.findViewById(R.id.child);
child.setClipToPadding(true);
child.setWillNotDraw(true);
child.addView(new UnclippedBlueView(rootView.getContext()));
};
static final ViewInitializer OUTLINE_CLIP_INIT = rootView -> {
View child = rootView.findViewById(R.id.child);
// ((ViewGroup)(child.getParent())).setBackgroundColor(Color.WHITE);
child.setOutlineProvider(new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
outline.setRect(OUTLINE_RECT);
}
});
child.setClipToOutline(true);
};
static final ViewInitializer OUTLINE_CLIP_AA_INIT = rootView -> {
View child = rootView.findViewById(R.id.child);
((ViewGroup) (child.getParent())).setBackgroundColor(Color.BLACK);
child.setOutlineProvider(new ViewOutlineProvider() {
Path mPath = new Path();
@Override
public void getOutline(View view, Outline outline) {
mPath.rewind();
// We're using the AA outline rect as a starting point, but shifting one of the
// vetices slightly to force AA for this not-quite-rectangle
mPath.moveTo(ANTI_ALIAS_OUTLINE_RECT.left, ANTI_ALIAS_OUTLINE_RECT.top);
mPath.lineTo(ANTI_ALIAS_OUTLINE_RECT.right, ANTI_ALIAS_OUTLINE_RECT.top);
mPath.lineTo(ANTI_ALIAS_OUTLINE_RECT.right, ANTI_ALIAS_OUTLINE_RECT.bottom);
mPath.lineTo(ANTI_ALIAS_OUTLINE_RECT.left + 1, ANTI_ALIAS_OUTLINE_RECT.bottom);
mPath.close();
outline.setPath(mPath);
}
});
child.setClipToOutline(true);
};
static final ViewInitializer CONCAVE_CLIP_INIT = rootView -> {
View child = rootView.findViewById(R.id.child);
((ViewGroup) (child.getParent())).setBackgroundColor(Color.BLACK);
child.setOutlineProvider(new ViewOutlineProvider() {
Path mPath = new Path();
@Override
public void getOutline(View view, Outline outline) {
mPath.rewind();
mPath.addRect(CONCAVE_OUTLINE_RECT1.left, CONCAVE_OUTLINE_RECT1.top,
CONCAVE_OUTLINE_RECT1.right, CONCAVE_OUTLINE_RECT1.bottom,
Path.Direction.CW);
mPath.addRect(CONCAVE_OUTLINE_RECT2.left, CONCAVE_OUTLINE_RECT2.top,
CONCAVE_OUTLINE_RECT2.right, CONCAVE_OUTLINE_RECT2.bottom,
Path.Direction.CW);
outline.setPath(mPath);
assertTrue(outline.canClip());
}
});
child.setClipToOutline(true);
};
static final ViewInitializer CLIP_BOUNDS_CLIP_INIT =
view -> view.setClipBounds(CLIP_BOUNDS_RECT);
static BitmapVerifier makeClipVerifier(Rect blueBoundsRect) {
// very high error tolerance, since all these tests care about is clip alignment
return new RectVerifier(Color.WHITE, Color.BLUE, blueBoundsRect, 75);
}
static BitmapVerifier makeConcaveClipVerifier() {
return new RegionVerifier()
.addVerifier(CONCAVE_TEST_RECT1, new RectVerifier(Color.BLACK, Color.BLUE,
CONCAVE_OUTLINE_RECT1, 75))
.addVerifier(CONCAVE_TEST_RECT2, new RectVerifier(Color.BLACK, Color.BLUE,
CONCAVE_OUTLINE_RECT2, 75));
}
static BitmapVerifier makeAAClipVerifier(Rect blueBoundsRect) {
return new AntiAliasingVerifier(Color.BLACK, Color.BLUE, blueBoundsRect);
}
@Test
public void testSimpleUnclipped() {
createTest()
.addLayout(R.layout.blue_padded_layout, null)
.runWithVerifier(makeClipVerifier(FULL_RECT));
}
@Test
public void testSimpleBoundsClip() {
createTest()
.addLayout(R.layout.blue_padded_layout, BOUNDS_CLIP_INIT)
.runWithVerifier(makeClipVerifier(BOUNDS_RECT));
}
@Test
public void testSimpleClipBoundsClip() {
createTest()
.addLayout(R.layout.blue_padded_layout, CLIP_BOUNDS_CLIP_INIT)
.runWithVerifier(makeClipVerifier(CLIP_BOUNDS_RECT));
}
@Test
public void testSimplePaddingClip() {
createTest()
.addLayout(R.layout.blue_padded_layout, PADDING_CLIP_INIT)
.runWithVerifier(makeClipVerifier(PADDED_RECT));
}
// TODO: add tests with clip + scroll, and with interesting combinations of the above
@Test
public void testSimpleOutlineClip() {
// NOTE: Only HW is supported
createTest()
.addLayout(R.layout.blue_padded_layout, OUTLINE_CLIP_INIT, true)
.runWithVerifier(makeClipVerifier(OUTLINE_RECT));
// SW ignores the outline clip
createTest()
.addLayout(R.layout.blue_padded_layout, OUTLINE_CLIP_INIT, false)
.runWithVerifier(makeClipVerifier(FULL_RECT));
}
@Test
public void testAntiAliasedOutlineClip() {
// NOTE: Only HW is supported
createTest()
.addLayout(R.layout.blue_padded_layout, OUTLINE_CLIP_AA_INIT, true)
.runWithVerifier(makeAAClipVerifier(ANTI_ALIAS_OUTLINE_RECT));
}
@Test
public void testOvalOutlineClip() {
// As of Android T, Outline clipping is enabled for all shapes.
createTest()
.addLayout(R.layout.blue_padded_layout, view -> {
view.setOutlineProvider(new ViewOutlineProvider() {
Path mPath = new Path();
@Override
public void getOutline(View view, Outline outline) {
mPath.reset();
mPath.addOval(0, 0, view.getWidth(), view.getHeight(),
Path.Direction.CW);
outline.setPath(mPath);
assertTrue(outline.canClip());
}
});
view.setClipToOutline(false); // should do nothing
})
.runWithVerifier(makeClipVerifier(FULL_RECT));
}
@Test
public void testConcaveOutlineClip() {
// As of Q, Outline#setPath (previously called setConvexPath) no longer throws on a concave
// path, but it does not result in clipping, which is only supported when explicitly calling
// one of the other setters. (hw no-op's the arbitrary path, and sw doesn't support Outline
// clipping.)
// As of T, path clipping is enabled for all Outline shapes.
createTest()
.addLayout(R.layout.blue_padded_layout, CONCAVE_CLIP_INIT, true)
.runWithVerifier(makeConcaveClipVerifier());
}
}