diff options
author | Michael Hoisie <hoisie@google.com> | 2024-04-26 00:39:59 -0700 |
---|---|---|
committer | Copybara-Service <copybara-worker@google.com> | 2024-04-26 00:40:46 -0700 |
commit | 41a9904a946afa40cd4b623ec088e9d018c0cb03 (patch) | |
tree | f49d0f3d9e0723849726f3e6fc4cb336c5a4b9e6 | |
parent | b551e5049d6c88c79fa5e51150aa546dd49d78aa (diff) | |
download | robolectric-41a9904a946afa40cd4b623ec088e9d018c0cb03.tar.gz |
Fix HW rendering in Android V
ShadowNativeBaseRecordingCanvas was missing the `callNativeMethodsByDefault`
annotation param, which meant that all the native methods were no-ops in
Android V.
Add some additional tests to capture this issue.
PiperOrigin-RevId: 628318882
3 files changed, 102 insertions, 11 deletions
diff --git a/integration_tests/nativegraphics/src/test/java/org/robolectric/integrationtests/nativegraphics/HardwareAcceleratedActivityRenderTest.java b/integration_tests/nativegraphics/src/test/java/org/robolectric/integrationtests/nativegraphics/HardwareAcceleratedActivityRenderTest.java index 3828aa087..7f8e26578 100644 --- a/integration_tests/nativegraphics/src/test/java/org/robolectric/integrationtests/nativegraphics/HardwareAcceleratedActivityRenderTest.java +++ b/integration_tests/nativegraphics/src/test/java/org/robolectric/integrationtests/nativegraphics/HardwareAcceleratedActivityRenderTest.java @@ -1,29 +1,73 @@ package org.robolectric.integrationtests.nativegraphics; import static android.os.Build.VERSION_CODES.S; +import static com.google.common.truth.Truth.assertThat; import android.app.Activity; +import android.graphics.Bitmap; +import android.graphics.Color; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.view.PixelCopy; +import android.view.View; +import android.view.ViewGroup.LayoutParams; +import android.view.Window; import android.view.WindowManager; +import android.widget.FrameLayout; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; -import org.robolectric.android.controller.ActivityController; import org.robolectric.annotation.Config; @RunWith(RobolectricTestRunner.class) @Config(minSdk = S) public class HardwareAcceleratedActivityRenderTest { @Test - public void setupHardwareAcceleratedActivity() { - // This will exercise much of the HardwareRenderer / RenderNode / RecordingCanvas native code. - ActivityController<Activity> controller = Robolectric.buildActivity(Activity.class); - controller - .get() - .getWindow() - .setFlags( - WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, - WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED); - controller.setup(); + public void hardwareAcceleratedActivity_setup() throws Exception { + // Setting up an Activity is a smoke test that exercises much of the HardwareRenderer / + // RenderNode / RecordingCanvas native code. + Robolectric.setupActivity(HardwareAcceleratedActivity.class); + } + + @Test + public void hardwareAcceleratedActivity_pixelCopy() throws Exception { + System.setProperty("robolectric.pixelCopyRenderMode", "hardware"); + try { + HardwareAcceleratedActivity activity = + Robolectric.setupActivity(HardwareAcceleratedActivity.class); + Window window = activity.getWindow(); + View decorView = window.getDecorView(); + Bitmap bitmap = + Bitmap.createBitmap(decorView.getWidth(), decorView.getHeight(), Bitmap.Config.ARGB_8888); + CountDownLatch latch = new CountDownLatch(1); + PixelCopy.request( + window, bitmap, copyResult -> latch.countDown(), new Handler(Looper.getMainLooper())); + latch.await(1, TimeUnit.SECONDS); + assertThat(bitmap.getPixel(100, 100)).isEqualTo(Color.RED); + } finally { + System.clearProperty("robolectric.pixelCopyRenderMode"); + } + } + + static class HardwareAcceleratedActivity extends Activity { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + // TODO(hoisie): manually setting these flags should not be required. Robolectric should + // set them automatically by default (they have been default since ICS). + getWindow() + .setFlags( + WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, + WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED); + FrameLayout frameLayout = new FrameLayout(this); + frameLayout.setLayoutParams( + new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); + frameLayout.setBackgroundColor(Color.RED); + setContentView(frameLayout); + } } } diff --git a/integration_tests/nativegraphics/src/test/java/org/robolectric/integrationtests/nativegraphics/ShadowNativeHardwareRendererTest.java b/integration_tests/nativegraphics/src/test/java/org/robolectric/integrationtests/nativegraphics/ShadowNativeHardwareRendererTest.java index 2b6c48d7b..89d8e675c 100644 --- a/integration_tests/nativegraphics/src/test/java/org/robolectric/integrationtests/nativegraphics/ShadowNativeHardwareRendererTest.java +++ b/integration_tests/nativegraphics/src/test/java/org/robolectric/integrationtests/nativegraphics/ShadowNativeHardwareRendererTest.java @@ -1,9 +1,21 @@ package org.robolectric.integrationtests.nativegraphics; import static android.os.Build.VERSION_CODES.Q; +import static android.os.Build.VERSION_CODES.S; +import static com.google.common.truth.Truth.assertThat; +import android.graphics.Bitmap; +import android.graphics.Color; import android.graphics.HardwareRenderer; +import android.graphics.Paint; +import android.graphics.PixelFormat; +import android.graphics.RecordingCanvas; +import android.graphics.RenderNode; +import android.media.Image; +import android.media.Image.Plane; +import android.media.ImageReader; import android.view.Choreographer; +import android.view.Surface; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; @@ -24,4 +36,38 @@ public class ShadowNativeHardwareRendererTest { // HardwareRenderer.nHackySetRTAnimationsEnabled. Ensure that RNG is loaded if this happens. var unused = Choreographer.getInstance(); } + + @Test + @Config(minSdk = S) + public void imageReader_readsRenderedDisplayList() { + int width = 100; + int height = 100; + + try (ImageReader imageReader = + ImageReader.newInstance(width, height, PixelFormat.RGBA_8888, 1)) { + HardwareRenderer renderer = new HardwareRenderer(); + RenderNode displayList = createDisplayList(width, height); + Surface surface = imageReader.getSurface(); + renderer.setSurface(surface); + Image nativeImage = imageReader.acquireNextImage(); + renderer.setContentRoot(displayList); + renderer.createRenderRequest().syncAndDraw(); + Plane[] planes = nativeImage.getPlanes(); + Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + bitmap.copyPixelsFromBuffer(planes[0].getBuffer()); + surface.release(); + assertThat(bitmap.getPixel(50, 50)).isEqualTo(Color.RED); + } + } + + private static RenderNode createDisplayList(int width, int height) { + RenderNode renderNode = new RenderNode("RedNode"); + renderNode.setPosition(0, 0, width, height); + RecordingCanvas canvas = renderNode.beginRecording(); + Paint paint = new Paint(); + paint.setColor(Color.RED); + canvas.drawRect(0, 0, width, height, paint); + renderNode.endRecording(); + return renderNode; + } } diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeBaseRecordingCanvas.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeBaseRecordingCanvas.java index 005b51905..b87bbe40d 100644 --- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeBaseRecordingCanvas.java +++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeBaseRecordingCanvas.java @@ -21,6 +21,7 @@ import org.robolectric.versioning.AndroidVersions.U; value = BaseRecordingCanvas.class, minSdk = Q, shadowPicker = Picker.class, + callNativeMethodsByDefault = true, isInAndroidSdk = false) public class ShadowNativeBaseRecordingCanvas extends ShadowNativeCanvas { |