How to force an image to draw outside of its boundaries in Jetpack Compose

Taken from here https://unsplash.com/photos/bGtETSIqIhk

Imagine a case when you need to put an image as in the picture below.

Looks clean, isn't?

And not like this:

I've found three ways to achieve it. But only one suited me because of the limited time.

@Composable
private fun ImageWithoutTopBounds(@DrawableRes id: Int) {
val bitmap = ImageBitmap.imageResource(id = id)

Canvas(
modifier = Modifier
.fillMaxSize()
) {
val canvasWidth = size.width.toInt()
val canvasHeight = size.height.toInt()

val imageHeight = bitmap.height
val imageWidth = bitmap.width

// magic calculations

drawImage(
image = bitmap,
srcOffset = IntOffset(),
dstOffset = IntOffset(),
srcSize = IntSize(),
dstSize = IntSize(),
)
}
}

2. Another complex to math way is to use Image with custom ContentScale and offset. I failed this one too :(

@Composable
private fun ImageWithoutTopBounds(@DrawableRes id: Int) {
val painter = painterResource(id = id)
val calculatedYOffset = ...// another magic
val calculatedXYScale = ...

Image(
modifier = Modifier
.fillMaxSize()
.offset(y = calculatedYOffset),
painter = painter,
contentScale = object : ContentScale {
override fun computeScaleFactor(srcSize: Size, dstSize: Size): ScaleFactor =
ScaleFactor(calculatedXYScale.x, calculatedXYScale.y)
}
)
}

3. And the last solution which suited me 100% is to use things out of the box - wrapContentXXX with unbound set to true. Quote from docs:

Allow the content to measure at its desired size without regard for the incoming maximum constraints

While second parameter alignment will anchor it, Top/Bottom for wrapContentHeight and Start/End for wrapContentWidth or mix of both when its wrapContentSize.

@Composable
private fun ImageWithoutTopBounds(@DrawableRes id: Int) {
val painter = painterResource(id = id)
Image(
modifier = Modifier
.wrapContentSize(align = Alignment.BottomCenter, unbounded = true) // here it is
.align(Alignment.Center),
painter = painter,
contentScale = FixedScale(1f) // if your image too big
)
}

Here some other examples:

.wrapContentWidth(align = Alignment.Start, unbounded = true)
.wrapContentHeight(align = Alignment.Bottom, unbounded = true)
.wrapContentWidth(align = Alignment.End, unbounded = true)
.wrapContentHeight(align = Alignment.Top, unbounded = true)
.wrapContentSize(align = Alignment.TopCenter, unbounded = true)

Cheers :)

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store