https://kotlinlang.org logo
#compose
Title
# compose
b

Brian G

01/17/2022, 5:26 PM
Is there anyway to render a composable at a lower resolution than the screen? For making pixelated UI. E.g. render it at 360x640 and upscale it (no anti-aliasing) to the screen's native 720x1280 I've tried using Modifier.scale(2f), but I still get native-pixel-sized rendering artifacts.
E.g. these horizontal lines between tiles.
a

allan.conda

01/17/2022, 5:29 PM
Perhaps
Modifier.graphicsLayer
could be useful for you?
b

Brian G

01/17/2022, 5:30 PM
Modifier.scale(2f)
Just calls
Modifier.graphicsLayer(scaleX=2f, scaleY=2f)
It scales my
Dp
size correctly, but it's still drawing at native resolution, as you can see by the rendering artifacts. In the above screenshot I'm using
scale(4f)
and then drawing some bitmaps.
You can also tell it's not really scaling it, by drawing Text, the text is still drawn at native resolution
I want to tell Compose to actually render using fewer pixels, then only scale up to fill the space.
Another example, I'm using a 4-pixel wide PNG to draw a window border, but Compose is not even scaling my PNG uniformly. This is inside a box with
scale(4f)
to make the "pixels" bigger.
I found the issue... I was usign Alignment.Center to fit the scaled output inside the box. This was creating the rendering artifacts. I fixed it by using transform origin, like:
graphicsLayer(scaleX=2f, scaleY=2f, transformOrigin=TransformOrigin(0f, 0f))
and
Alignment.TopStart
Though, I would still love to find a way to actual render it at a lower resolution, so for e.g. even Material ripple effect would appear pixelated
a

allan.conda

01/17/2022, 6:23 PM
How about wrapping your composable in an AndroidView of fixed dp size and then scale that. I'm not sure. This is interesting though, saving this in case you find a solution :)
a

Albert Chang

01/18/2022, 12:16 AM
You can provide a lower
LocalDensity
.
b

Brian G

02/05/2022, 10:47 PM
I got this perfect, using a combination of two things: 1. Provide a LocalDensity of 1f, so that 1.dp == 1 integer pixel 2. Use graphicsLayer to scale my entire app to give a virtual resolution of approx 360x360 (exact size depends on screen resolution) I configure this once at the very root of my app, and now all of compose, including UI widgets, Canvas, bitmaps, etc. all render pixel-perfect with no artifacts :)
Copy code
Box(
                Modifier.align(Alignment.TopStart)
                    .graphicsLayer(
                        scaleX = dpScale,
                        scaleY = dpScale,
                        transformOrigin = TransformOrigin(0f, 0f)
                    )
                    .fillMaxSize(1/dpScale)
            ) {
                CompositionLocalProvider(LocalDensity provides Density(1f)) {
                    // App code here
                }
            }