Status Update
Comments
cl...@google.com <cl...@google.com>
gu...@gmail.com <gu...@gmail.com> #2
This is a particularly hard device to come by - do you happen to have access to the device? If so could you provide us with the output of: adb shell dumpsys media.camera > info.txt
Thanks!
il...@gmail.com <il...@gmail.com> #3
Stacktrace:
Caused by: java.lang.IllegalArgumentException: Can not get supported output size under supported maximum for the format: 34
at androidx.camera.camera2.internal.SupportedSurfaceCombination.getSupportedOutputSizes(SupportedSurfaceCombination.java:355)
at androidx.camera.camera2.internal.SupportedSurfaceCombination.getSuggestedResolutions(SupportedSurfaceCombination.java:197)
at androidx.camera.camera2.internal.Camera2DeviceSurfaceManager.getSuggestedResolutions(Camera2DeviceSurfaceManager.java:198)
at androidx.camera.core.CameraX.calculateSuggestedResolutions(CameraX.java:943)
at androidx.camera.core.CameraX.bindToLifecycle(CameraX.java:293)
at androidx.camera.lifecycle.ProcessCameraProvider.bindToLifecycle(ProcessCameraProvider.java:227)
Below are some findings based on our debugging
When Dex is connected
previewConfig.getMaxResolution() is returning "731x411" as maxSize.
Inside Preview.Builder.build() -> Default_MAX_resolution is set to "CameraX.getSurfaceManager().getPreviewSize()" which is 731x411
this is being picked as maxSize.
While rendering maxSize is 731x411 and minSize is 640x480 and below are available outputSizes
0 = {Size@11860} "4032x3024"
1 = {Size@11861} "3984x2988"
2 = {Size@11862} "4032x2268"
3 = {Size@11863} "3024x3024"
4 = {Size@11864} "2976x2976"
5 = {Size@11865} "3840x2160"
6 = {Size@11866} "3264x2448"
7 = {Size@11867} "4032x1960"
8 = {Size@11868} "2880x2160"
9 = {Size@11869} "3264x1836"
10 = {Size@11870} "2160x2160"
11 = {Size@11871} "2560x1440"
12 = {Size@11872} "2224x1080"
13 = {Size@11873} "2048x1152"
14 = {Size@11874} "1920x1080"
15 = {Size@11875} "1440x1080"
16 = {Size@11876} "1088x1088"
17 = {Size@11877} "1280x720"
18 = {Size@11878} "1024x768"
19 = {Size@11879} "1056x704"
20 = {Size@11880} "960x720"
21 = {Size@11881} "960x540"
22 = {Size@11882} "720x720"
23 = {Size@11883} "800x450"
24 = {Size@11884} "720x480"
25 = {Size@11885} "640x480"
26 = {Size@11886} "352x288"
27 = {Size@11887} "320x240"
28 = {Size@11888} "256x144"
29 = {Size@11889} "176x144"
and couldn't find any size in this range.
When Dex not connected
minsize = 640x480
maxsize = 1920x1080
0 = {Size@11836} "4032x3024"
1 = {Size@11837} "3984x2988"
2 = {Size@11838} "4032x2268"
3 = {Size@11839} "3024x3024"
4 = {Size@11840} "2976x2976"
5 = {Size@11841} "3840x2160"
6 = {Size@11842} "3264x2448"
7 = {Size@11843} "4032x1960"
8 = {Size@11844} "2880x2160"
9 = {Size@11845} "3264x1836"
10 = {Size@11846} "2160x2160"
11 = {Size@11847} "2560x1440"
12 = {Size@11848} "2224x1080"
13 = {Size@11849} "2048x1152"
14 = {Size@11850} "1920x1080"
15 = {Size@11851} "1440x1080"
16 = {Size@11852} "1088x1088"
17 = {Size@11853} "1280x720"
18 = {Size@11854} "1024x768"
19 = {Size@11855} "1056x704"
20 = {Size@11856} "960x720"
21 = {Size@11857} "960x540"
22 = {Size@11858} "720x720"
23 = {Size@11859} "800x450"
24 = {Size@11860} "720x480"
25 = {Size@11861} "640x480"
26 = {Size@11862} "352x288"
27 = {Size@11863} "320x240"
28 = {Size@11864} "256x144"
29 = {Size@11865} "176x144"
and we have 12 available sizes in this range
Camera2DeviceSurfaceManager.java:: getPreviewSize()
mCameraSupportedSurfaceCombinationMap.get(cameraId).getSurfaceDefinition().getPreviewSize() = "1920x1080"
cameraId=0
me...@gmail.com <me...@gmail.com> #4
The issue root cause is that CameraX will default filter out sizes smaller than 640x480. For Preview, the max size will be limited to under display size. I checked the HW spec info for the issue related devices. Display size of FUJITSU F-04J/F-05J is 360x640. That will result int that no size exists in the conditions that is larger or equal to 640x480 and smaller or equal to 360x640.
A temporary workaround for this situation is to use Preview.Builder#setTargetResolution() to set a size smaller than 640x480 to bypass the problem.
For device FUJITSU arrowsM04, I checked its HW spec info and its display size I found is 1280x720. It seems that the problem should not exist in the device.
Could you confirm that the problem exist on arrowsM04 device? What will be the returned value when using Display#getRealSize to obtain the display size?
co...@gmail.com <co...@gmail.com> #5
> A temporary workaround for this situation is to use Preview.Builder#setTargetResolution() to set a size smaller than 640x480 to bypass the problem.
OK. I will try it.
> Could you confirm that the problem exist on arrowsM04 device?
We receive the crash report (Crashlytics) that this crash has occurred on arrowsM04.
We don't have this device so we can't confirm that the problem really exist on arrowsM04.
> What will be the returned value when using Display#getRealSize to obtain the display size?
We can't investigate it for the same reason.
Thank you.
ja...@gmail.com <ja...@gmail.com> #6
This issue happened on devices that the display size is smaller than 640x480. In original auto-resolution mechanism, supported sizes smaller than 640x480 will be default filter out.
The auto-resolution mechanism encodes the guaranteed configurations tables in CameraDevice#createCaptureSession(SessionConfiguration). It defines that the PREVIEW size is the small one of the device display size and 1080p. The PREVIEW size will be the maximal size limitation for Preview use case. The reason it limits the size to display size and 1080p is the stream output in display size or 1080p has been able to provide good enough preview quality. Therefore, auto-resolution mechanism will limit the selected size to be smaller than the small one of the device display size and 1080p.
With above two conditions, in this issue, all sizes smaller than 640x480 have been filter out, therefore, there is no size smaller than the display size 320x240 can be selected to use. And cause the exception.
Solution:
When the display size is smaller than 640x480, auto-resolution mechanism won't filter out those small sizes smaller than 640x480. This makes those small size be left and can be selected for the Preview use case on small display devices.
The solution has been merged and will be included in next CameraX release.
sa...@rapido.bike <sa...@rapido.bike> #7
Hello.
This crash still occurs.
- CAMERAX VERSION: 1.0.0-beta4
- ANDROID OS BUILD NUMBER: Android 7.1.1
- DEVICE NAME: FUJITSU F-02H
We receive following crash report from FUJITSU F-02H. So far We have received this crash report only from F-02H.
java.lang.IllegalArgumentException
Can not get supported output size under supported maximum for the format: 34
androidx.camera.camera2.internal.SupportedSurfaceCombination.getSupportedOutputSizes (SupportedSurfaceCombination.java:349)
androidx.camera.camera2.internal.SupportedSurfaceCombination.getSuggestedResolutions (SupportedSurfaceCombination.java:197)
androidx.camera.camera2.internal.Camera2DeviceSurfaceManager.getSuggestedResolutions (Camera2DeviceSurfaceManager.java:198)
androidx.camera.core.CameraX.calculateSuggestedResolutions (CameraX.java:949)
androidx.camera.core.CameraX.bindToLifecycle (CameraX.java:351)
androidx.camera.lifecycle.ProcessCameraProvider.bindToLifecycle (ProcessCameraProvider.java:230)
(our application's package name).CameraFragment.bindCameraUseCases (CameraFragment.java:174)
ro...@gmail.com <ro...@gmail.com> #8
Could you help to provide the following information to clarify the issue?
1. Is the full name of the device Fujitsu Arrows NX F-02H that has a 1440x2560 display?
2. Please help to provide the supported output sizes of ImageFormat.PRIVATE that is obtained by StreamConfigurationMap#getOutputSizes(int).
za...@gmail.com <za...@gmail.com> #9
- Is the full name of the device Fujitsu Arrows NX F-02H that has a 1440x2560 display?
Yes
- Please help to provide the supported output sizes of ImageFormat.PRIVATE that is obtained by StreamConfigurationMap#getOutputSizes(int).
Since we don't have this device, we'll try to collect this information in the next version of our app. The next version will be released later this month.
ma...@gmail.com <ma...@gmail.com> #10
Hello.
- Please help to provide the supported output sizes of ImageFormat.PRIVATE that is obtained by StreamConfigurationMap#getOutputSizes(int).
We have collected the output of the device where the crash occurs.
Device1
- Model : arrows Be F-05J
- Android Version : 7.1.1
- Supported output sizes of ImageFormat.PRIVATE
CameraId 0: 480x480
CameraId 1: 2048x1536 ,1920x1080 ,1280x720 ,960x720 ,640x480 ,320x240 ,176x144
Device2
- Model : Fujitsu arrows M04
- Android Version : 7.1.1
- Supported output sizes of ImageFormat.PRIVATE
CameraId 0: 480x480
CameraId 1: 2048x1536 ,1920x1080 ,1280x720 ,960x720 ,640x480 ,320x240 ,176x144
Additional Information
CameraX version : 1.0.0-beta04
We collect the supported output sizes by following code.
val errorString = buildString {
append("The supported output sizes of ImageFormat.PRIVATE: ")
(requireContext().getSystemService(Context.CAMERA_SERVICE) as CameraManager).apply {
cameraIdList.forEachIndexed { index, cameraId ->
val msg = if (VERSION.SDK_INT >= VERSION_CODES.M) {
val configurationMap =
getCameraCharacteristics(cameraId).get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
val sizes = configurationMap?.getOutputSizes(ImageFormat.PRIVATE)
"CameraId $index: ${sizes?.joinToString(" ,")}"
} else {
"CameraId $index: This device version is under M."
}
append(msg)
}
}
}
ba...@gmail.com <ba...@gmail.com> #11
pr...@gmail.com <pr...@gmail.com> #12
I tried to find the device specs and both 720x1280
size display. For the camera id 0 device, it is a different case that the display size is larger than 640x480
but the device only supports a 480x480
size. The case also caused the same IllegalArgumentException and was also fixed by 1.0.0-beta04
release. Before 480x480
size would be filtered out and then caused the IllegalArgumentException. After it was merged, the 640x480
size threshold was removed and then the 480x480
size would be kept and selected to use.
It looks like 1.0.0-beta04
release had been used to collect the supported sizes information. But the issue should have been fixed by 1.0.0-beta04
release. Did you only check the device model name to collect the supported sizes information or collect the information when the IllegalArgumentException issue happens again?
CameraX's 1.0.0-beta04
version. Maybe you can also consider to upgrade to the latest 1.0.0-rc01
version for your application. Thanks.
ma...@gmail.com <ma...@gmail.com> #13
Did you only check the device model name to collect the supported sizes information or collect the information when the IllegalArgumentException issue happens again?
We collect informations only from the device on which IllegalArgumentException happened.
Our latest app uses CameraX version 1.0.0-beta10
and this issue still occurres.
However we don't receive crash report from Fujitsu arrows Be F-05J
or Fujitsu arrows M04
so far. (This doesn't mean this issue is fixed on these devices because our app is heavily rely on camera so these device's user wouldn't use our app anymore.)
Instead, we receive crash report from
- Model : Fujitsu F-03K
- Android Version : 7.1.2
- Supported output sizes of ImageFormat.PRIVATE
CameraId 0 : 480x480
CameraId 1 : 2048x1536 ,1920x1080 ,1280x720 ,960x720 ,640x480 ,320x240 ,176x144
ri...@gmail.com <ri...@gmail.com> #14
I missed some settings when I simulated the issue by robolectric test so that I was not able to reproduce it. Now, I can reproduce the issue if the device only supports one 480x480 resolution. I'm working on the solution and target to make it included in next release.
ep...@gmail.com <ep...@gmail.com> #15
Branch: androidx-main
commit 69d15dff7bb857ee33a0f643ff42a0f8bc475ab2
Author: charcoalchen <charcoalchen@google.com>
Date: Fri Jan 08 18:30:03 2021
Fixed IllegalArgumentException issue happened when all preview supported sizes are smaller than 640x480 and display size is larger than 640x480.
Do not filter out sizes smaller than 640x480 when all preview supported sizes are smaller than 640x480 and display size is larger than 640x480.
Relnote:"Fixed IllegalArgumentException issue happened when all preview supported sizes are smaller than 640x480 and display size is larger than 640x480."
Bug: 150506192
Test: SupportedSurfaceCombinationTest
Change-Id: I2a63ce8e2ad42a9cc060c8635ac3603bf440b1ec
M camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/SupportedSurfaceCombination.java
M camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSurfaceCombinationTest.java
ra...@gmail.com <ra...@gmail.com> #16
xy...@gmail.com <xy...@gmail.com> #17
ho...@gmail.com <ho...@gmail.com> #18
br...@gmail.com <br...@gmail.com> #19
am...@gmail.com <am...@gmail.com> #20
to...@gmail.com <to...@gmail.com> #21
+1
lo...@gmail.com <lo...@gmail.com> #22
Hey folks, please stop adding +1 comments and use the dedicated star button at the top of the page instead.
That'll save some noise and avoid all the "stargazers" from getting unactionable emails.
Thank you.
ta...@mercury.com <ta...@mercury.com> #23
Here's a terrible, terrible hack to get the blur RenderEffect
for a Compose canvas.
The hack is terrible but it appears to work. You have been warned.
so...@gmail.com <so...@gmail.com> #24
de...@gmail.com <de...@gmail.com> #25
ya...@gmail.com <ya...@gmail.com> #26
ma...@gmail.com <ma...@gmail.com> #27
Hey folks, please stop adding +1 comments and use the dedicated star button at the top of the page instead.
That'll save some noise and avoid all the "stargazers" from getting unactionable emails.
Thank you.
[Deleted User] <[Deleted User]> #29
ap...@google.com <ap...@google.com> #31
Branch: androidx-main
commit 87b47565c30d75faf827b482a2dbad95c3016ca7
Author: Nader Jawad <njawad@google.com>
Date: Tue Aug 17 19:56:23 2021
Introduce BlurredEdgeTreatment API
Relnote: "Added BlurredEdgeTreatment API
to simplify blur use cases into more
commonly used combinations of clip flags
and TileModes. Most use cases involve
either letting blurred content render
outside the original content bounds
and blurring regions outside these bounds
with transparent black, or clipping content
to content bounds sampling the closest edge
for blur kernels that extend beyond content
bounds."
Bug: 166927547
Test: Added BlurTest
Change-Id: I6b4b7966920855374275ae7ea950b310fa28efd0
M compose/ui/ui/api/current.txt
M compose/ui/ui/api/public_plus_experimental_current.txt
M compose/ui/ui/api/restricted_current.txt
M compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/BlurSample.kt
A compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/BlurTest.kt
M compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/draw/Blur.kt
ap...@google.com <ap...@google.com> #32
Branch: androidx-main
commit a04a6e81fdeba710a3790d30e113d203dd3ff390
Author: Nader Jawad <njawad@google.com>
Date: Tue Aug 03 17:37:24 2021
More RenderEffect support
Relnote: "Added support for RenderEffect
in compose desktop.
Introduced OffsetEffect as well as
the blur modifier as a simple way to
introduce blur visual effects to a portion
of the composition hierarchy."
Fixed order of operations issue on compose
desktop that prevented clipping from working
properly with saveLayer.
Bug: 166927547
Test: Added tests to RenderEffectTest/GraphicsLayerTest/
AndroidRenderEffectTest
Change-Id: I0f6aa293db2bf34f5ed2aa7499a97332dacf73fc
M compose/ui/ui-graphics/api/current.txt
M compose/ui/ui-graphics/api/public_plus_experimental_current.txt
M compose/ui/ui-graphics/api/restricted_current.txt
M compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/AndroidRenderEffectTest.kt
M compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidRenderEffect.android.kt
M compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/RenderEffect.kt
M compose/ui/ui-graphics/src/desktopMain/kotlin/androidx/compose/ui/graphics/DesktopTileMode.desktop.kt
A compose/ui/ui-graphics/src/desktopMain/kotlin/androidx/compose/ui/graphics/RenderEffect.desktop.kt
M compose/ui/ui-graphics/src/test/java/androidx/compose/ui/graphics/RenderEffectTest.kt
M compose/ui/ui/api/current.txt
M compose/ui/ui/api/public_plus_experimental_current.txt
M compose/ui/ui/api/restricted_current.txt
A compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/BlurSample.kt
A compose/ui/ui/samples/src/main/res/drawable/circus.jpg
M compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/GraphicsLayerModifierTest.kt
M compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/GraphicsLayerTest.kt
A compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/draw/Blur.kt
M compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/SkijaLayer.desktop.kt
M compose/ui/ui/src/desktopTest/kotlin/androidx/compose/ui/platform/SkijaLayerTest.kt
ap...@google.com <ap...@google.com> #33
Branch: androidx-main
commit af8e89a54fb3173251a43251bd8a0bda2a126c66
Author: Nader Jawad <njawad@google.com>
Date: Fri Jul 30 18:46:49 2021
Add RenderEffect API
Relnote: "Introduced RenderEffect API
that can be optionally configured on
a Modifier.graphicsLayer to alter
the contents of the layer itself. This
can be used to blur contents of a composable
and child composables within a composition
hierarchy."
Bug: 166927547
Test: Added tests to RenderEffectTest/GraphicsLayerTest/
AndroidRenderEffectTest
Change-Id: I47c4d5ecc801f35e632d2062e03c756f564a2db5
M compose/ui/ui-graphics/api/current.txt
M compose/ui/ui-graphics/api/public_plus_experimental_current.txt
M compose/ui/ui-graphics/api/restricted_current.txt
A compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/AndroidRenderEffectTest.kt
A compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidRenderEffect.android.kt
A compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/RenderEffect.kt
A compose/ui/ui-graphics/src/test/java/androidx/compose/ui/graphics/RenderEffectTest.kt
M compose/ui/ui/api/current.ignore
M compose/ui/ui/api/current.txt
M compose/ui/ui/api/public_plus_experimental_current.txt
M compose/ui/ui/api/restricted_current.ignore
M compose/ui/ui/api/restricted_current.txt
M compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidLayoutDrawTest.kt
M compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/GraphicsLayerTest.kt
M compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/DeviceRenderNode.android.kt
M compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/RenderNodeApi23.android.kt
M compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/RenderNodeApi29.android.kt
M compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/RenderNodeLayer.android.kt
M compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/ViewLayer.android.kt
M compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/GraphicsLayerModifier.kt
M compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/GraphicsLayerScope.kt
M compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeWrapper.kt
M compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/OwnedLayer.kt
M compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/SkijaLayer.desktop.kt
M compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/LayoutNodeTest.kt
ap...@google.com <ap...@google.com> #34
Branch: androidx-main
commit 1d626519ace91a88f1a08d02b24d3741dea10b95
Author: Nader Jawad <njawad@google.com>
Date: Thu Jul 29 15:23:06 2021
Introduce TileMode.Decal
Relnote: "Add TileMode.Decal support
which is useful in defining edge
behavior for blur based RenderEffects."
Bug: 166927547
Test: Added tests to TileModeTest and
AndroidTileModeTest
Change-Id: I7e8ed0c4eb2490ef3cd0032b5952d0962f489354
M compose/ui/ui-graphics/api/current.txt
M compose/ui/ui-graphics/api/public_plus_experimental_current.txt
M compose/ui/ui-graphics/api/restricted_current.txt
A compose/ui/ui-graphics/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/AndroidTileModeTest.kt
M compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/AndroidTileMode.android.kt
M compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/TileMode.kt
M compose/ui/ui-graphics/src/test/java/androidx/compose/ui/graphics/TileModeTest.kt
el...@gmail.com <el...@gmail.com> #35
nj...@google.com <nj...@google.com>
cs...@google.com <cs...@google.com> #36
🎉🎉🎉
fa...@gmail.com <fa...@gmail.com> #37
when will it be ready for use? And short guideline would great. Thank you for the fast reaction!
da...@gmail.com <da...@gmail.com> #38
Nice, which version of compose will have this?
Is it android 12 only or backported? Thanks and well done!
nj...@google.com <nj...@google.com> #39
This API is supported on Android 12 only and is a no-op for older API levels.
Blur is supported as a Modifier API and common usage would look like the following:
Box(modifier = Modifier.size(100.dp).blur(30.dp)) {
// child composable
}
[Deleted User] <[Deleted User]> #40
se...@gmail.com <se...@gmail.com> #41
It's already been released with Compose 1.1.0-alpha03
ru...@gmail.com <ru...@gmail.com> #42
sh...@gmail.com <sh...@gmail.com> #43
is it supported in android < 12
si...@gmail.com <si...@gmail.com> #44
ri...@gmail.com <ri...@gmail.com> #45
I think this question should be reopened? It doesn't support versions below Android S, as a non-system ui framework, this is very disappointing.
ja...@gmail.com <ja...@gmail.com> #46
And Kotlin Multiplatform info should be great too
sh...@gmail.com <sh...@gmail.com> #47
The Blur Modifier in compose currently only supports the API 31, hence it is of no use. I tried to create a blur Modifier by first drawing the content on the bitmap canvus and the blur it and draw it on target canvus. but it seems compose does expose the draw(canvus) like in ViewWorld. Currently I looked the docs of Test ScreenShot, Magnifier but both of these use onGloballyPositioned to capture the piece of bitmap from LocalView. Hence it doesn't do what I intended it to do.
There is another solution that involves Creating a Blury View and then use composed to make a modifier (But I don't like this solution.). If anybody knows how to achieve it please do tell me.
mg...@gmail.com <mg...@gmail.com> #48
bl...@gmail.com <bl...@gmail.com> #49
cs...@google.com <cs...@google.com> #50
To
On the "glassmorphism" effect, could send a link to exactly what effect do you mean?
ga...@gmail.com <ga...@gmail.com> #51
+1
wo...@gmail.com <wo...@gmail.com> #52
mg...@gmail.com <mg...@gmail.com> #53
The best I can show you without putting to much effort on it, it is some images examples:
Here, the card receives an alpha color and the elements located behind it receive a level o blur.
As you move the front element, the back ones dynamically interact with it.
And the effect, as you can see, can be stacked.
ca...@usp.br <ca...@usp.br> #54
Glassmorphism would be great since it opens several UI possibilities (that right now seems only feasible for iOS). In Brazil, Android is predominant and there's a current trend among UXs to adopt glassmorphic like effects. The only thing holding us back is the implementation friction.
Plenty use cases here:
ra...@gmail.com <ra...@gmail.com> #55
We need it
cs...@google.com <cs...@google.com> #56
Regarding
mg...@gmail.com <mg...@gmail.com> #57
mg...@gmail.com <mg...@gmail.com> #58
Searching through the internet, you can find quite a lot of examples on how to implement background blur. All the examples, although different in execution, uses the same logic:
1 - You choose a root for you effect;
2 - You choose the view you want to apply the effect on;
3 - You observe the view relative position into the view tree;
4 - When the view is showing itself on the screen, fully or partially, you start the following computation:
a - Get the matrix dimension of the desired view;
b - Generates a bitmap from the dimensions of the view;
c - Scale down to a proper scale so the next steps don't consume too much resources;
d - With the scaled bitmap, apply algorithms of image treatment to achieve the blur effect;
e - With the blurred bitmap, paint it on a canvas based on the dimensions of the view;
f - finally, display the blurred view on the place of a normal view (with transparent on it).
As you can see, this process is kind of a cheat one. It can work well in some cases, but most of them are just terrible. The more your components move in screen, the harder it becomes to achieve performance and better looking effects.
Another downside of this method is its compatibility with the composable way of working. On old style android, each fragment / activity is its own view. But, that's not the case for compose: the whole thing is a single view. There are some ways of transforming Compose component -> Compose View -> Android View, creating some sort of old android view tree, but that's just nasty and prune to bad results.
After experimenting with all of these, I was looking into the Composable Canvas, having a lot fun with it by the way, and I realized the presence of a very interesting effect which can be applied to the drawing: blend mode.
Blend mode has some interesting visual effects on the drawing. It computes a mix of RGB channels so the back composable interacts with the children component mixing the visuals. It's almost the same principle used in the background blur, but instead of pixels effect, it's a color effect. The blend mode, funny enough, just breaks with the new Blur Effect from composable.
Finally, a last but interesting discover, is the
mg...@gmail.com <mg...@gmail.com> #60
[Deleted User] <[Deleted User]> #61
ba...@gmail.com <ba...@gmail.com> #62
Dear Android Team,
Current design i.e Modifier.blur(50.dp)
supports blur on the composable itself not underneath. But its use is too much limited. What most of the people here and elsewhere are
looking for overlay blur which is not supported.
I don't think it is a too hard to even comprehend kind of request. If you guys believe it is. Kindly give us a response of why it is that hard to implemented a overlay blur.
da...@gmail.com <da...@gmail.com> #63
Android 12 introduced support for RenderEffect
s including blurring, even though out-of-the-box it would blur the View
itself, and not any content underneath it — so it's not a backdrop blur (in CSS terms, it's like filter: blur()
and not backdrop-filter: blur()
).
However, internally, Views are backed by RenderNode
s, which ultimately apply RenderEffects. By leveraging RenderNodes, it's possible to implement backdrop blurring, and I've done that with Compose. Note that it's for Android 12 onwards.
For anyone interested, the implementation is at the end of the post.
Sample usage
It works in a producer–consumer manner, where the producer is any composable with Modifier.backdropContent
(which feeds the composable into the Backdrop
) and the consumer is any composable with Modifier.backdrop
(which takes content from the specified Backdrop
and draws it behind the composable, applying a RenderEffect
to it). The Backdrop
class thus serves to connect the producers and the consumers and it also stores the content being produced — the backdrop. You obtain an instance of Backdrop
using the rememberBackdrop
function.
To further simplify usage on the consumer part, I also implemented Modifier.blurredSurface
, which accepts a surfaceColor
, blurRadius
, saturation
, and a border stroke
, and sets up the backdrop with the appropriate RenderEffect
automatically. The following example should show a list (to be populated) and a blurred bar overlaid on top of the list.
- Note: I haven't tested the example, but the implementation works in my app.
@Composable
fun BackdropBlurDemo() {
val backdrop = rememberBackdrop()
val listState = rememberLazyListState()
val showDivider = listState.canScrollForward
Box(modifier = Modifier.fillMaxSize()) {
Box(
modifier = Modifier
.fillMaxWidth()
.requiredHeight(56.dp)
.blurredSurface(backdrop) {
BlurredSurfaceDefaults.parameters(
strokeAlpha = when {
showDivider -> BlurredSurfaceDefaults.DefaultStrokeAlpha
else -> 0f
},
strokeAlphaAnimationSpec = spring()
)
}
)
LazyColumn(
state = listState,
modifier = Modifier
.matchParentSize()
.backdropContent(backdrop)
) {
// Load some items
}
}
}
Implementation
Notes:
- Some
import
s may be missing or unordered, I joined several files to post the code here. - The implementation depends on the artifact
androidx.compose.ui:ui-util
.
import android.graphics.RenderNode
import android.os.Build
import androidx.annotation.RequiresApi
import androidx.compose.animation.core.AnimationSpec
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.material3.DividerDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.*
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.layout.*
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.util.packFloats
import androidx.compose.ui.util.unpackFloat1
import androidx.compose.ui.util.unpackFloat2
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract
import kotlin.math.roundToInt
import kotlin.reflect.KProperty
@Immutable
data class BlurredSurfaceParameters(
val surfaceColor: Color,
val blurRadius: Dp,
val saturation: Float,
val stroke: DpStroke?
) {
@JvmField
internal val paint = Paint().also { paint ->
paint.colorFilter = ColorFilter.colorMatrix(
colorMatrix = when (val saturation = saturation) {
1f -> {
MakeOpaqueColorMatrix
}
else -> ColorMatrix().also { colorMatrix ->
colorMatrix.setToSaturation(saturation)
colorMatrix *= MakeOpaqueColorMatrix
}
}
)
}
}
@Immutable
object BlurredSurfaceDefaults {
const val DefaultSurfaceOpacity = 0.5f
@JvmStatic val DefaultBlurRadius = 24.dp
const val DefaultSaturation = 1.3f
const val DefaultStrokeAlpha = 0.7f
@Composable
fun parameters(
surfaceColor: Color = MaterialTheme.colorScheme.surface,
surfaceOpacity: Float = DefaultSurfaceOpacity,
blurRadius: Dp = DefaultBlurRadius,
saturation: Float = DefaultSaturation,
strokeAlpha: Float = DefaultStrokeAlpha,
strokeAlphaAnimationSpec: AnimationSpec<Float>? = null,
stroke: DpStroke? = DpStroke(
width = DividerDefaults.Thickness,
color = MaterialTheme.colorScheme.outlineVariant,
parameters = StrokeParameters(
alignment = -1f,
alpha = when (strokeAlphaAnimationSpec) {
null -> strokeAlpha
else -> animateFloatAsState(
targetValue = strokeAlpha,
animationSpec = strokeAlphaAnimationSpec,
visibilityThreshold = 1f / 255f,
label = "blurredSurface strokeAlpha"
).value
},
blendMode = BlendMode.Luminosity
)
)
) = BlurredSurfaceParameters(
surfaceColor = surfaceColor.copy(alpha = surfaceOpacity),
blurRadius = blurRadius,
saturation = saturation,
stroke = stroke
)
}
fun Modifier.blurredSurface(
backdrop: Backdrop,
parameters: @DisallowComposableCalls @Composable () -> BlurredSurfaceParameters = {
BlurredSurfaceDefaults.parameters()
}
) = composed {
val currentParameters by rememberUpdatedState(parameters())
val currentDensity by rememberUpdatedState(LocalDensity.current)
val background: DrawScope.() -> Unit = remember {
{ drawRect(currentParameters.surfaceColor.copy(alpha = 1f)) }
}
remember {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
backdrop(
backdrop = backdrop,
background = background,
paint = { currentParameters.paint },
renderEffect = {
with(currentDensity) {
val radiusPx = currentParameters.blurRadius.toPx()
BlurEffect(
radiusX = radiusPx,
radiusY = radiusPx,
edgeTreatment = TileMode.Decal
)
}
}
).drawBehind {
drawRect(currentParameters.surfaceColor)
}
} else {
drawBehind(background)
}
}
.stroke(currentParameters.stroke)
}
private val MakeOpaqueColorMatrix = ColorMatrix(
floatArrayOf(
1f, 0f, 0f, 0f, 0f,
0f, 1f, 0f, 0f, 0f,
0f, 0f, 1f, 0f, 0f,
0f, 0f, 0f, 0f, 255f
)
)
@Composable
fun rememberBackdrop(): Backdrop =
remember { Backdrop() }
@RequiresApi(Build.VERSION_CODES.S)
fun Modifier.backdropContent(
backdrop: Backdrop,
paint: () -> Paint? = Backdrop.DefaultPrePaint
): Modifier {
val paintState = derivedStateOf(structuralEqualityPolicy(), paint)
return composed {
remember(backdrop) {
backdrop.ContentNode(paintState = paintState)
}.modifier
}
}
@RequiresApi(Build.VERSION_CODES.S)
fun Modifier.backdrop(
backdrop: Backdrop,
inset: Dp = 0.dp,
background: (DrawScope.() -> Unit)? = null,
paint: () -> Paint? = Backdrop.DefaultPostPaint,
renderEffect: () -> RenderEffect
): Modifier {
val paintState = derivedStateOf(structuralEqualityPolicy(), paint)
val renderEffectState = derivedStateOf(structuralEqualityPolicy(), renderEffect)
return composed {
remember(backdrop) {
BackdropNode(
backdrop = backdrop,
paintState = paintState,
renderEffectState = renderEffectState,
initialInsetDp = inset.value
)
}
.also { it.background = background }
.also { it.insetDp = inset.value }
.modifier
}
}
class Backdrop internal constructor() {
@Immutable
internal companion object {
@JvmStatic internal val DefaultPrePaint: () -> Paint? = { null }
@JvmStatic internal val DefaultPostPaint: () -> Paint? = { null }
@JvmStatic private val DefaultPrePaintValue = NativePaint()
}
private var contentNodeHead: ContentNode? = null
private var contentNodeTail: ContentNode? = null
@OptIn(ExperimentalContracts::class)
internal inline fun forEachNode(
block: (ContentNode) -> Unit
) {
contract {
callsInPlace(block)
}
contentNodeHead?.forEach(block)
}
internal inner class ContentNode
/**
* Creates a [ContentNode] that is added to this [Backdrop] when remembered
* and removed when forgotten.
*
* Always create inside a [remember] block.
*/
@RequiresApi(Build.VERSION_CODES.Q)
constructor(
paintState: State<Paint?>
) :
RememberObserver {
@RequiresApi(Build.VERSION_CODES.Q)
override fun onRemembered() {
append()
}
override fun onForgotten() {
remove()
}
override fun onAbandoned() {
remove()
}
var renderNode: RenderNode? = null
private set
val paint by paintState
var position by mutableOffsetStateDelegateOf(Offset.Zero)
private set
/**
* The [Modifier] to be used in [backdropContent].
*/
@RequiresApi(Build.VERSION_CODES.Q)
@JvmField
val modifier = Modifier
.onPlaced { position = it.positionInRoot() }
.drawWithContent {
renderNode?.let { renderNode ->
renderNode.setUseCompositingLayer(
true,
paint?.asFrameworkPaint() ?: DefaultPrePaintValue
)
val (width, height) = size.round()
renderNode.setPosition(0, 0, width, height)
withNode(renderNode) { drawContent() }
}
}
private var previous: ContentNode? = null
private var next: ContentNode? = null
@OptIn(ExperimentalContracts::class)
inline fun forEach(
block: (ContentNode) -> Unit
) {
contract {
callsInPlace(block)
}
var node = this
while (true) {
block(node)
node = node.next ?: break
}
}
@RequiresApi(Build.VERSION_CODES.Q)
private fun append() {
if (renderNode != null)
return
renderNode = RenderNode("content")
this@Backdrop.contentNodeTail = apply {
when (val tail = this@Backdrop.contentNodeTail) {
null -> {
contentNodeHead = this
}
else -> {
tail.next = this
this.previous = tail
}
}
}
}
private fun remove() {
if (renderNode == null)
return
renderNode = null
val previous = this.previous
val next = this.next
previous?.next = next
next?.previous = previous
if (this@Backdrop.contentNodeHead === this)
this@Backdrop.contentNodeHead = next
if (this@Backdrop.contentNodeTail === this)
this@Backdrop.contentNodeTail = previous
}
}
}
@RequiresApi(Build.VERSION_CODES.S)
private class BackdropNode(
private val backdrop: Backdrop,
paintState: State<Paint?>,
renderEffectState: State<RenderEffect>,
initialBackground: (DrawScope.() -> Unit)? = null,
initialInsetDp: Float
) {
private var position by mutableOffsetStateDelegateOf(Offset.Zero)
private val backdropNode = RenderNode("backdrop")
val renderEffect by renderEffectState
val paint by paintState
var background by mutableStateOf(initialBackground)
var insetDp by mutableFloatStateOf(initialInsetDp)
/**
* The [Modifier] to be used in [backdrop].
*/
@JvmField
val modifier = Modifier
.onPlaced { position = it.positionInRoot() }
.drawBehind {
val backdropNode = backdropNode
val (width, height) = size.round()
val inset = insetDp.dp.roundToPx()
backdropNode.setRenderEffect(renderEffect.asAndroidRenderEffect())
backdropNode.setPosition(
inset,
inset,
width - inset,
height - inset
)
backdropNode.withRecording { blurCanvas ->
backdrop.forEachNode { contentNode ->
contentNode.renderNode?.let { renderNode ->
val (sx, sy) = contentNode.position
val (dx, dy) = this@BackdropNode.position
val tx = sx - dx - inset
val ty = sy - dy - inset
blurCanvas.translate(+tx, +ty)
blurCanvas.drawRenderNode(renderNode)
blurCanvas.translate(-tx, -ty)
}
}
}
with(drawContext.canvas.androidCanvas) {
val saveCount = saveLayer(
0f,
0f,
width.toFloat(),
height.toFloat(),
paint?.asFrameworkPaint()
)
background?.invoke(this@drawBehind)
drawRenderNode(backdropNode)
restoreToCount(saveCount)
}
}
}
@Stable
private inline fun Size.round(): IntSize =
IntSize(
width = width.roundToInt(),
height = height.roundToInt()
)
@OptIn(ExperimentalContracts::class)
@RequiresApi(Build.VERSION_CODES.Q)
private inline fun <R> RenderNode.withRecording(
block: (Canvas) -> R
): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return try {
block(beginRecording())
} finally {
endRecording()
}
}
@OptIn(ExperimentalContracts::class)
@RequiresApi(Build.VERSION_CODES.Q)
private inline fun <T : DrawScope, R> T.withNode(
renderNode: RenderNode,
draw: T.() -> R
): R {
contract {
callsInPlace(draw, InvocationKind.EXACTLY_ONCE)
}
val composeCanvas = drawContext.canvas
val nativeCanvas = composeCanvas.androidCanvas
val result: R
renderNode.withRecording { rnCanvas ->
composeCanvas.androidCanvas = rnCanvas
result = draw()
composeCanvas.androidCanvas = nativeCanvas
}
nativeCanvas.drawRenderNode(renderNode)
return result
}
private fun mutableOffsetStateDelegateOf(value: Offset) = OffsetStateDelegate(value = value)
@JvmInline
private value class OffsetStateDelegate private constructor(
private val state: MutableLongState
) {
constructor(value: Offset) : this(
state = mutableLongStateOf(value.toLongValue())
)
operator fun getValue(thisObj: Any?, property: KProperty<*>): Offset =
state.longValue.let { boundsValue ->
Offset(
x = unpackFloat1(boundsValue),
y = unpackFloat2(boundsValue)
)
}
operator fun setValue(
thisObj: Any?,
property: KProperty<*>,
value: Offset
) {
state.longValue = value.toLongValue()
}
@Immutable
private companion object {
@Suppress("NOTHING_TO_INLINE")
@Stable
private inline fun Offset.toLongValue() = packFloats(x, y)
}
}
License: No specific license, I'm offering this for any kind of use or modification.
sh...@gmail.com <sh...@gmail.com> #64
Hi, I’m the author of the Toolkit library, which contains a custom blur modifier that can blur the background behind any composable in real time. It works on any version of Android.
I wrote an article that shows how to use my library and how to create stunning glassmorphic designs for your Android apps. You can read it here: Blurring the Lines: How to Achieve a Glassmorphic Design with Jetpack Compose.
link:
I hope you find my library and article helpful. If you have any feedback or suggestions, please let me know.
Description
It was mentioned in a Dev YouTube video that Compose will be design system agnostic. In order to achieve that, it would make sense to natively support blurring as some design systems use it. Also, there are designers that don't necessary use MD while designing apps for Android,
and in that case if they use blur usually they either have to find an alternative
or the devs have a really hard time getting it to work consistently across all devices.
Ideally, blurring would be supported on all container layouts such as CardView, ConstraintLayout etc. (all that inherit from ViewGroup?). The API should be something like this:
Modifier.blur(radius = 16.dp, dynamic = true)
Dynamic = true/false would mean either realtime blurring in moving/scrolling layouts or one-time-blur on static content to minimize performance impact when dynamic blurring is not needed.
As for the current problems of blurring in Android - the only way to implement it is using RenderScript or an external library (from which most rely on RenderScript anyway).
When using it the API is usually complicated and after some testing on different devices it seems
as the performance impact depends on the chipset where Snapdragon devices have no perceivable lag and for ex. Kirin and Exynos devices drop frames to a point where it is not feasable to keep blur in UI.