Closed Vulama closed 4 months ago
yep, i have the same problem. Im with a lazycolumn and i see a strange effect when you start to scroll the images or a texts. Anyone know how to fix it?
E/AndroidRuntime: FATAL EXCEPTION: main Process: com.mycompany.myapp, PID: 13052 java.lang.RuntimeException: Failed to copy pixels of the given bitmap! at com.skydoves.cloudy.CloudyKt.drawBitmapWithPixelCopy$lambda$7(Cloudy.kt:281) at com.skydoves.cloudy.CloudyKt.$r8$lambda$X2Nw-BPO6_ca_Hnlrx5we4hyARg(Unknown Source:0) at com.skydoves.cloudy.CloudyKt$$ExternalSyntheticLambda0.onPixelCopyFinished(Unknown Source:6) at android.view.PixelCopy$1.run(PixelCopy.java:191) at android.os.Handler.handleCallback(Handler.java:873) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:233) at android.app.ActivityThread.main(ActivityThread.java:7225) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:499) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:962) Suppressed: kotlinx.coroutines.DiagnosticCoroutineContextException: [androidx.compose.ui.platform.MotionDurationScaleImpl@157fa8e, androidx.compose.runtime.BroadcastFrameClock@8a122af, StandaloneCoroutine{Cancelling}@78746bc, AndroidUiDispatcher@d8e9645]#
with just normal Column
Seems to occur when you scroll at anything but a turtle's pace
Can reproduce with a LazyColumn
TL;DR this is caused by PixelCopy, can't copy scrollable content, because they are out of bounds of view containing Window. View.drawToBitmap might work, but will have to disable hardware acceleration for App, Activity or Glide Request(more about glide here
Hello the root cause of your problem is hardware acceleration. After about Android Oreo bitmaps are drawn directly to hardware. If we want to copy pixels from a view using canvas.draw, view's cache or other software methods won't work we have to use PixelCopy. Here is the code in Cloudy.kt that does just that:
You can see contents of drawToBitmap here: https://android.googlesource.com/platform/frameworks/support/+/android-room-release/core/ktx/src/main/java/androidx/core/view/View.kt#188
Now let's see how drawBitmapWithPixelCopy works: https://github.com/skydoves/Cloudy/blob/db2bd225232ea4e1a7f72f8da608b023de7e7435/cloudy/src/main/kotlin/com/skydoves/cloudy/Cloudy.kt#L258-L287
As you can see the exception: RuntimeException("Failed to copy pixels of the given bitmap!")
is thrown when PixelCopy.request is not successful.
https://cs.android.com/android/platform/superproject/+/master:frameworks/base/graphics/java/android/view/PixelCopy.java;l=254-282;drc=9bdd2e6151aa7a70bb4a12e91e7f68959b6334cb
If we look inside the request function, we can see that the new surface object is created from source Window:
final ViewRootImpl root = source.peekDecorView().getViewRootImpl();
if (root != null) {
surface = root.mSurface;
You can check window's width and height to see that it is close to the screen dimensions, it does not matter if you have scrolling content inside of that window. If you have some image or some other view inside of Cloudy composable that is out of the bounds of Window you will get an Exception, because the content will say that its location is at x=1500, y= 3000, when bounds of your window are at x=1080, y = 2800. so when srcRect is passed with (1500, 3000) PixelCopy will return an integer 1 which is constant ERROR_UNKNOWN: https://cs.android.com/android/platform/superproject/+/master:frameworks/base/graphics/java/android/view/PixelCopy.java;l=46?q=PixelCopy
It is not actually PixelCopy class that does the actual copying of Pixels, the chain of method calls goes deep into the native code starting fromThreadedRenderer.copySurfaceInto(source, srcRect, dest);
https://cs.android.com/android/platform/superproject/+/master:frameworks/base/graphics/java/android/view/PixelCopy.java;l=187?q=PixelCopy
copySurfaceInto is actually not a method of ThreadedRenderer it is from its parent HardwareRenderer as you can see: https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/view/ThreadedRenderer.java;l=63?q=ThreadedRenderer
HardwareRenderer calls native method: https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/graphics/java/android/graphics/HardwareRenderer.java;l=1588?q=HardwareRenderer
Here as you can see inside of HardwareRenderer.cpp copySurfaceInto call is delegated to RenderProxy: https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/graphics/java/android/graphics/HardwareRenderer.java;l=1132?q=HardwareRenderer
RenderProxy gets an Instance of RenderThread, then gets a ReadBack field from RenderThread Instance and calls copySurfaceInto function: https://cs.android.com/android/platform/superproject/+/master:frameworks/base/libs/hwui/renderthread/RenderProxy.cpp;l=374?q=RenderProxy
here is the method to get ReadBack: https://cs.android.com/android/platform/superproject/+/master:frameworks/base/libs/hwui/renderthread/RenderProxy.cpp;l=374?q=RenderProxy
And here we have the actual implementation of copySurfaceInto function in ReadBack class: https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/libs/hwui/Readback.cpp;l=56-268?q=ReadBack
Because this is native code deep within AOSP, I have not fully grasped it nor do I have a way to debug it. However here is the list of all lines that return Unknown Error:
You can read what software bitmap functions no longer work, because of hardware bitmaps: Glide doc
The same problem is happening on my side.
Here is the crash : Fatal Exception: java.lang.RuntimeException: Failed to copy pixels of the given bitmap! at com.skydoves.cloudy.CloudyKt.drawBitmapWithPixelCopy$lambda$7(Cloudy.kt:281) at android.view.PixelCopy$1.run(PixelCopy.java:191) at android.os.Handler.handleCallback(Handler.java:938) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:236) at android.app.ActivityThread.main(ActivityThread.java:8056) at java.lang.reflect.Method.invoke(Method.java) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:656) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:967)
Have the same problem
the issue still persists
+1
Hey guys, the new version 0.2.0 has been released, and now you can use Modifier.cloudy
modifier instead of the Cloudy
composable function. Now, it can be used inside the scrollable components, such as LazyColumn or LazyRow. For more details, check out the reference: https://github.com/skydoves/cloudy?tab=readme-ov-file#maintaining-blurring-effect-on-responsive-composable
Tested on: -- API 29 - Android 10 (Pixel 5 Android Studio emulator) -- API 33 - Android 13 (OnePlus 8 physical device)
Describe the Bug: When we try to use Cloudy in any structure that uses vertical scroll in any of the parents, the app crashes.
Error -> java.lang.RuntimeException: Failed to copy pixels of the given bitmap! Example of the code that crashes (this is tested on the blank project):
Expected Behavior: This should work in the scrollable structure.