From 80d2af36ad458b42bd921578377bca6f00f4e94b Mon Sep 17 00:00:00 2001 From: chrfalch Date: Thu, 15 Jun 2023 15:58:17 +0200 Subject: [PATCH] Android: Fixed so that we don't render children double when making image snapshot. Before rendering a view we're now setting any visible children to invisible to avoid view.draw(canvas) to render children - and then have to render children after rendering texture/surface views. This ruins opacity - now fixed. Closes [Android]: makeImageFromView doesn't capture react components that are overlapping with skia components #1633 --- .../skia/ViewScreenshotService.java | 41 +++++++++++++++---- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/package/android/src/main/java/com/shopify/reactnative/skia/ViewScreenshotService.java b/package/android/src/main/java/com/shopify/reactnative/skia/ViewScreenshotService.java index 16f1431a78..adb8104567 100644 --- a/package/android/src/main/java/com/shopify/reactnative/skia/ViewScreenshotService.java +++ b/package/android/src/main/java/com/shopify/reactnative/skia/ViewScreenshotService.java @@ -71,13 +71,35 @@ private static void renderViewToCanvas(Canvas canvas, View view, Paint paint) { canvas.save(); applyTransformations(canvas, view); - // Render view itself - view.draw(canvas); - // Draw children if the view has children if ((view instanceof ViewGroup)) { // Draw children ViewGroup group = (ViewGroup) view; + + // Hide visible children - this needs to be done because view.draw(canvas) + // will render all visible non-texture/surface views directly - causing + // views to be rendered twice - once by view.draw() and once when we + // enumerate children. We therefore need to turn off rendering of visible + // children before we call view.draw: + List visibleChildren = new ArrayList<>(); + for (int i = 0; i < group.getChildCount(); i++) { + View child = group.getChildAt(i); + if (child.getVisibility() == VISIBLE) { + visibleChildren.add(child); + child.setVisibility(View.INVISIBLE); + } + } + + // Draw ourselves + view.draw(canvas); + + // Enable children again + for (int i = 0; i < visibleChildren.size(); i++) { + View child = visibleChildren.get(i); + child.setVisibility(VISIBLE); + } + + // Draw children for (int i = 0; i < group.getChildCount(); i++) { View child = group.getChildAt(i); @@ -90,7 +112,7 @@ private static void renderViewToCanvas(Canvas canvas, View view, Paint paint) { tvChild.setOpaque(false); // <-- switch off background fill canvas.save(); - applyTransformations(canvas, view); + applyTransformations(canvas, child); // TextureView should use bitmaps with matching size, // otherwise content of the TextureView will be scaled to provided bitmap dimensions @@ -108,7 +130,7 @@ private static void renderViewToCanvas(Canvas canvas, View view, Paint paint) { try { PixelCopy.request(svChild, childBitmapBuffer, copyResult -> { canvas.save(); - applyTransformations(canvas, view); + applyTransformations(canvas, child); canvas.drawBitmap(childBitmapBuffer, 0, 0, paint); canvas.restore(); latch.countDown(); @@ -121,19 +143,20 @@ private static void renderViewToCanvas(Canvas canvas, View view, Paint paint) { Bitmap cache = svChild.getDrawingCache(); if (cache != null) { canvas.save(); - applyTransformations(canvas, view); + applyTransformations(canvas, child); canvas.drawBitmap(svChild.getDrawingCache(), 0, 0, paint); canvas.restore(); } } } else { // Regular views needs to be rendered again to ensure correct z-index - // order with texture views and surface views. This is a bit stupid - // it'll result in rendering regular views twice - but it is the only - // way we can possibly render both surrounding and child views + // order with texture views and surface views. renderViewToCanvas(canvas, child, paint); } } + } else { + // Draw ourselves + view.draw(canvas); } // Restore canvas