google / accompanist

A collection of extension libraries for Jetpack Compose
https://google.github.io/accompanist
Apache License 2.0
7.31k stars 592 forks source link

accompanist-drawablepainter: PictureDrawable doesn't scale #1766

Closed theojarrus closed 1 month ago

theojarrus commented 3 months ago

Describe the bug

PictureDrawable displayed using Compose Image and Accompanist DrawablePainter doesn't scale

To Reproduce

  1. Create PictureDrawable
    val picture = Picture()
    val pictureCanvas = picture.beginRecording(100, 100)
    pictureCanvas.drawCircle(50f, 50f, 50f, Paint())
    picture.endRecording()
    val drawable = PictureDrawable(picture)
  2. Show using Compose Image and Accompanist DrawablePainter
    Image(
    painter = rememberDrawablePainter(drawable),
    contentDescription = null,
    contentScale = ContentScale.FillHeight,
    modifier = Modifier.height(200.dp).border(2.dp, color = Color.Cyan)
    )
  3. See the image is not scaled

Expected behavior

Image scales depending on available size and scale strategy

Screenshots

Actual Expected (fixed using canvas#scale)
Screenshot 2024-04-01 at 16 30 03 Screenshot 2024-04-01 at 16 30 22

Environment

Additional context

Thoughts about fixing

In search of the reason why my image does not scale, I deepened into the source code. I found, that picture receives size in Picture#beginRecording, then getting long value from Picture#nativeBeginRecording, which also receives picture size, and saves this long value to PictureCanvas. After that, inside Picture#draw, which is called to draw picture on some canvas, Picture#nativeDraw is called. Don't know what magic happens then, but as the result picture is rendered using size recieved in Picture#beginRecording and not Drawable#setBounds. I managed to achieve the desired result using Canvas#scale, I don't know if that can cause performance problems, but at least it works. So, it can be fixed by overwriting DrawablePainter#onDraw for PictureDrawable, here's the example of working code:

// Create canvas with size bigger than picture size
Canvas(Modifier.height(200.dp).width(200.dp).border(2.dp, color = Color.Cyan)) {
   // Create simple PictureDrawable
   val picture = Picture()
   val pictureCanvas = picture.beginRecording(100, 100)
   pictureCanvas.drawCircle(50f, 50f, 50f, Paint())
   picture.endRecording()
   val drawable = PictureDrawable(picture)
   // Render PictureDrawable on canvas
   drawIntoCanvas { canvas ->
      // Set PictureDrawable bounds
      drawable.setBounds(0, 0, size.width.roundToInt(), size.height.roundToInt())
      // Scale canvas
      val scalex = size.width / drawable.intrinsicWidth
      val scaley = size.height / drawable.intrinsicHeight
      canvas.scale(scalex, scaley)
      // Draw PictureDrawable
      drawable.draw(canvas.nativeCanvas)
   }
}

Why found this bug

I'm writing library with runtime image generation, it uses Picture to render graphics and pass through app. As the result I want to show it somewhere using Compose Image, so I used accompanist-drawablepainter, but faced this issue.

theojarrus commented 2 months ago

@bentrengrove Check this issue please

PrimoDev23 commented 1 month ago

@theojarrus I created a PR for this https://github.com/google/accompanist/pull/1771 As soon as the review is done, it can be merged

bentrengrove commented 1 month ago

I mentioned this in the PR, but it is up to the drawable implementation how it scales, not the painter. So in this example you shouldn't be using fixed coords for defining your picture, you should be using the sizing information.

Something like:

val halfWidth = width/2f
val pictureCanvas = picture.beginRecording(width, height)
pictureCanvas.drawCircle(halfWidth, halfWidth, halfWidth, Paint())
theojarrus commented 1 month ago

@bentrengrove Understood your point, but I'm using PictureDrawable to store and share picture data through code. In this case, it is impossible to know exact size of final view. And also it can be changed, for example, by switching landscape orientation, so, in my opinion, the image size should be also changed, like ImageView does

theojarrus commented 1 week ago

@bentrengrove