accompanist icon indicating copy to clipboard operation
accompanist copied to clipboard

accompanist-drawablepainter: PictureDrawable doesn't scale

Open theojarrus opened this issue 1 year ago • 2 comments

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)
  1. 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)
)
  1. 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

  • Android OS version: API 34
  • Device: Emulator, Google Pixel 4
  • Accompanist version: 0.34.0

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 avatar Apr 01 '24 14:04 theojarrus

@bentrengrove Check this issue please

theojarrus avatar Apr 16 '24 13:04 theojarrus

@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

PrimoDev23 avatar May 23 '24 08:05 PrimoDev23

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())

bentrengrove avatar May 23 '24 20:05 bentrengrove

@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 avatar May 23 '24 20:05 theojarrus

@bentrengrove

theojarrus avatar Jun 27 '24 17:06 theojarrus