saket / telephoto

Building blocks for designing media experiences in Compose UI
https://saket.github.io/telephoto/
Apache License 2.0
869 stars 28 forks source link

Need SubsamplingScaleImageView.visibleFileRect(Rect fRect) analog #89

Open NatalySd opened 1 month ago

NatalySd commented 1 month ago

I need to get the visible area Rect of the source bitmap to save it into another one (android wallpapers app). It seems I need something like davemorissey SubsamplingScaleImageView.visibleFileRect(Rect fRect) in ZoomableState. I tried to look at contentTransformation & transformedContentBounds, but did not succeded in getting the exact rect which is shown. It would be nice if you add such method.

brinsche commented 1 month ago

This sounds similar to https://github.com/saket/telephoto/issues/64#issuecomment-2086222965 maybe the solution works for you as well?

NatalySd commented 1 month ago

Thanks, but this is different. Composable-to-bitmap api writes to a bitmap a piece of the screen and I need a piece of the original bitmap.

saket commented 1 month ago

Hmm, I would have expected transformedContentBounds to give you the bounds you're looking for.

https://github.com/saket/telephoto/blob/7015d6eed1c1ff876844682728912bcfb94773f9/zoomable/src/commonMain/kotlin/me/saket/telephoto/zoomable/RealZoomableState.kt#L179-L184

Are its numbers significantly off?

NatalySd commented 1 month ago

I made some experiments. I have bitmap 1920 x 1080 (aspect ratio = 1,777777777777778) and screensize = 2000 x 1200 (aspect ratio = 1,666666666666667). I show it full size with ContentScale.Crop and want to get the visible area from the bitmap.

If I pan to top left corner, then visible bitmap crop area will be left=0, top=0, right=1800, bottom=1080 According to logs I have contentSize=Size(1920.0, 1080.0) offset=Offset(0.0, 0.0) scaleX=1.1111112 scaleY=1.1111112 transformedContentBounds=Rect.fromLTRB(0.0, 0.0, 2133.3, 1200.0)

If I pan to bottom right corner , then visible bitmap crop area will be left=120, top=0, right=1920, bottom=1080 According to logs I have contentSize=Size(1920.0, 1080.0) offset=Offset(-133.3, 0.0) scaleX=1.1111112 scaleY=1.1111112 transformedContentBounds=Rect.fromLTRB(-133.3, 0.0, 2000.0, 1200.0)

So I finnaly succedded to get this area correctly with the following code getCroppedRectFromBitmap( sourceBitmap, contentWidth = zoomableState.contentTransformation.contentSize.width, contentHeight = zoomableState.contentTransformation.contentSize.height, scaledRectWidth = imageContentSizeInDp.width.value * density.density, scaledRectHeight = imageContentSizeInDp.height.value * density.density, scaledOffsetX = zoomableState.contentTransformation.offset.x, scaledOffsetY = zoomableState.contentTransformation.offset.y, scaleX = zoomableState.contentTransformation.scale.scaleX, scaleY = zoomableState.contentTransformation.scale.scaleY, )

fun getCroppedRectFromBitmap( sourceBitmap:Bitmap, scaledRectWidth: Float, scaledRectHeight: Float, scaledOffsetX: Float, scaledOffsetY: Float, scaleX: Float, scaleY: Float, ): Bitmap? { val scale = max(scaleX, scaleY) val left = abs(scaledOffsetX / scale).toInt() val top = abs(scaledOffsetY / scale).toInt() var width = (scaledRectWidth / scale).toInt() width = min(width, sourceBitmap.width - left) var height = (scaledRectHeight / scale).toInt() height = min(height, sourceBitmap.height - top) return Bitmap.createBitmap(sourceBitmap, left, top, width, height) }