Closed naikon33 closed 1 month ago
@naikon33 can you show a example of how you're using a modifier?
yes , here is part of the code that is responsible for zooming out and zooming in
`Capturable( controller = captureController, modifier = Modifier .size(capturableW.value, capturableH.value) .graphicsLayer( translationX = offsetCanvasX, translationY = offsetCanvasY, ), onCaptured = { bitmap, error -> if (bitmap != null) { saveToFile( context = context, bitmap = bitmap.asAndroidBitmap() ) { filePath -> onSave(filePath) } }
if (error != null) {
// Error occurred. Handle it!
}
}
) {
BoxWithConstraints(
Modifier
.size(646.dp, 318.dp)
.clip(RoundedCornerShape(16.dp))
.background(Color.White)
.graphicsLayer(
translationX = offsetCanvasX,
translationY = offsetCanvasY,
scaleX = contentScale,
scaleY = contentScale
)
.onGloballyPositioned { layoutCoordinates ->
drawingAreaSize.value = layoutCoordinates.size
/*selectedDrawables.forEach { drawableState ->
val drawable =
ContextCompat.getDrawable(context, drawableState.id)
drawable?.let {
val width = it.intrinsicWidth * drawableState.scale
val height =
it.intrinsicHeight * drawableState.scale
drawable.setBounds(
0,
0,
width.toInt(),
height.toInt()
)
}
}*/
}`
I can’t understand why saving with distance doesn’t work on devices with API<29 but it works on devices with API>29
I'm using the library version: implementation "dev.shreyaspatil:capturable:1.0.3"
Can you try with the latest version please and with the latest modifier API?
I used the latest versions and even installed the latest version implementation "dev.shreyaspatil:capturable:2.1.0" . But this did not help, everything is also cut off in API below 29
Are you maintaining modifier order properly with the latest version's implementation?
yes, if this were not so, then saving with distance would not work correctly in API>29
This is my order:
Capturable(
controller = captureController,
modifier = Modifier
.size(capturableW.value, capturableH.value)
.graphicsLayer(
translationX = offsetCanvasX,
translationY = offsetCanvasY,
),
onCaptured = { bitmap, error ->
// This is captured bitmap of a content inside Capturable Composable.
if (bitmap != null) {
saveToFile(
context = context,
bitmap = bitmap.asAndroidBitmap()
) { filePath ->
onSave(filePath)
}
// Bitmap is captured successfully. Do something with it!
}
if (error != null) {
// Error occurred. Handle it!
}
}
) {
BoxWithConstraints(
Modifier
.size(646.dp, 318.dp)
.clip(RoundedCornerShape(16.dp))
.background(Color.White)
.graphicsLayer(
translationX = offsetCanvasX,
translationY = offsetCanvasY,
scaleX = contentScale,
scaleY = contentScale
)
.onGloballyPositioned { layoutCoordinates ->
drawingAreaSize.value = layoutCoordinates.size
/*selectedDrawables.forEach { drawableState ->
val drawable =
ContextCompat.getDrawable(context, drawableState.id)
drawable?.let {
val width = it.intrinsicWidth * drawableState.scale
val height =
it.intrinsicHeight * drawableState.scale
drawable.setBounds(
0,
0,
width.toInt(),
height.toInt()
)
}
}*/
}
.pointerInput(Unit) {
detectTapGestures { offset ->
if (selectedSegment == TabItem.Text) {
isTextMode = true
textFields = textFields + TextFieldData(
"",
offset.x,
offset.y,
editable = true
)
} else {
val touchedLines =
lines.firstOrNull { lineGroup ->
lineGroup.any { line ->
isPointCloseToLine(
offset,
line
)
}
}
if (touchedLines != null) {
selectedLines =
touchedLines.also { lineGroup ->
lines.forEach {
it.forEach { line ->
line.isSelected.value =
false
}
}
lineGroup.forEach {
it.isSelected.value = true
}
}
} else {
lines.forEach {
it.forEach { line ->
line.isSelected.value = false
}
}
selectedLines = null
}
}
selectedDrawables.forEachIndexed { i, ds ->
ds.isSelected.value = false
}
lastMovedIndex = -1
}
}
.pointerInput(Unit) {
if (selectedSegment != TabItem.Text) {
detectTransformGestures { centroid, pan, zoom, rotation ->
scope.launch {
val newScale = contentScale * zoom
contentScale = newScale.coerceIn(0.43f, 5f)
val centroidOffsetX =
centroid.x - (1041 / 2)
val centroidOffsetY = centroid.y - (603 / 2)
val proposedOffsetX =
offsetCanvasX + pan.x - (zoom - 1) * centroidOffsetX
val proposedOffsetY =
offsetCanvasY + pan.y - (zoom - 1) * centroidOffsetY
val maxOffsetX =
max(0f, 1041 * (contentScale - 1) / 2)
val maxOffsetY =
max(0f, 603 * (contentScale - 1) / 2)
offsetCanvasX = proposedOffsetX.coerceIn(
-maxOffsetX,
maxOffsetX
)
offsetCanvasY = proposedOffsetY.coerceIn(
-maxOffsetY,
maxOffsetY
)
}
}
}
}
.pointerInput(selectedLines) {
if (selectedSegment == TabItem.Lines) {
detectTransformGestures { centroid, pan, zoom, rotation ->
val groupCenter =
selectedLines?.let { findGroupCenter(it) }
?: return@detectTransformGestures
val degrees =
rotation * (180.0 / Math.PI).toFloat() * 0.1f
selectedLines?.forEach { line ->
line.offsetX.value += pan.x
line.offsetY.value += pan.y
line.rotateAround(groupCenter, degrees)
}
}
}
}
) {
ActionButton(
modifier = Modifier
.width(156.dp)
.height(40.dp),
title = stringResource(R.string.save).toUpperCase(Locale.current),
isFilled = true,
onClick = {
captureController.capture()
}
)
and here's my implementation:
dependencies {
implementation "androidx.appcompat:appcompat:1.6.1"
implementation "androidx.drawerlayout:drawerlayout:1.1.1"
implementation 'androidx.core:core-ktx:1.13.1'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1'
implementation 'androidx.activity:activity-compose:1.9.0'
implementation "androidx.compose.ui:ui:1.6.7"
implementation "androidx.compose.ui:ui-tooling-preview:1.6.7"
implementation "io.coil-kt:coil-compose:2.4.0"
implementation 'androidx.compose.material:material:1.6.7'
implementation "dev.shreyaspatil:capturable:2.1.0"
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
androidTestImplementation "androidx.compose.ui:ui-test-junit4:1.6.7"
debugImplementation "androidx.compose.ui:ui-tooling:1.6.7"
debugImplementation "androidx.compose.ui:ui-test-manifest:1.6.7"
}
Don't use Capturable
composable function which is deprecated now.
Use capturable()
modifier on the exact component which you need to capture and see if that works or not.
I did as in the example in a new way, it didn’t help, the edges are still cut off:
Column(
modifier = Modifier.capturable(captureController)
) {
BoxWithConstraints(
Modifier
.size(646.dp, 318.dp)
.clip(RoundedCornerShape(16.dp))
.background(Color.White)
.graphicsLayer(
translationX = offsetCanvasX,
translationY = offsetCanvasY,
scaleX = contentScale,
scaleY = contentScale
)
.onGloballyPositioned { layoutCoordinates ->
drawingAreaSize.value = layoutCoordinates.size
}
.pointerInput(Unit) {
}
) {
ActionButton(
modifier = Modifier
.width(156.dp)
.height(40.dp),
title = stringResource(R.string.save).toUpperCase(Locale.current),
isFilled = true,
onClick = {
scope.launch {
val bitmapAsync = captureController.captureAsync()
try {
val bitmap = bitmapAsync.await()
saveToFile(
context = context,
bitmap = bitmap.asAndroidBitmap()
) { filePath ->
onSave(filePath)
}
} catch (error: Throwable) {
// Error occurred, do something.
}
}
}
)
here is a photo of it still being cropped:
And this is what it looks like on canvas:
I'm not sure on this, but I think this is happening because of translation and scale applied via graphicsLayer()
modifier. Maybe that's causing cropping of the captured area. Just for the test, can you try removing that graphicsLayer()
for instance?
Well, graphicsLayer() is needed to zoom out and these distant places are cut off.P.s it turns out that the edges are cut off only in API 28; in other versions everything works correctly. Then why is the problem with API 28?
Probably fixed in v3.0.0
, please try and let us know if the issue re-occurs.
I use Capturable to capture the canvas and save it in the gallery. The problem occurs on devices with API<29 when I zoom out from the canvas with the graphicsLayer to add additional pictures. Distant parts are simply cut off and so on only in devices with API<29, but on devices above everything is captured perfectly without cutting, etc. How can this be fixed?