Android-TiffBitmapFactory
Android-TiffBitmapFactory copied to clipboard
TiffConverter.convertJpgTiffFd fails to convert image with a CantOpenFileException from cache.
I have a jetpack compose app with the following logic that triggers to allow user to select an image from the gallery and then copies it to cache:
val context = LocalContext.current
var progressString by remember { mutableStateOf<String?>(null) }
val launcher = rememberLauncherForActivityResult(contract = ActivityResultContracts.GetContent()) {
if (it != null) {
context.contentResolver.openInputStream(it)?.use { input ->
val file = File(context.cacheDir, "copiedimage.jpg")
FileOutputStream(file).use { output ->
val buffer = ByteArray(4 * 1024)
var read: Int
while (input.read(buffer).also { read = it } != -1) {
output.write(buffer, 0, read)
}
output.flush()
}
val fd = context.contentResolver.openFileDescriptor(file.toUri(), "r")
if (fd != null) {
TiffConverterObject.convertToTiffAndSaveInCache(fd, context.cacheDir) { progress ->
progressString = "${progress * 100}%"
}
}
}
}
}
Button(
modifier = Modifier
.width(250.dp)
.padding(bottom = 8.dp),
onClick = {
launcher.launch("image/*")
}
) {
Text(text = "Choose image from gallery")
}
I see the copiedimage.jpeg
in the cache directory and I've saved it to my desktop and opened it so I know that's being saved correctly:
This is my TiffConverterObject class which uses this library to convert the image to tiff but it keeps crashing returning a CantOpenFileException
:
object TiffConverterObject {
fun convertToTiffAndSaveInCache(inputFD: ParcelFileDescriptor, outputFD: File, progress: (Double) -> Unit) {
val options = ConverterOptions()
options.throwExceptions = true
options.availableMemory = (128 * 1024 * 1024).toLong()
options.compressionScheme = CompressionScheme.LZW
options.appendTiff = false
val file = File.createTempFile("output-temp", ".tiff", outputFD)
val inputFd = inputFD.fd
inputFD.close()
val fd = App.instance.contentResolver.openFileDescriptor(file.toUri(), "rwt")
if (fd != null) {
val outputFd = fd.fd
fd.close()
val saved = TiffConverter.convertJpgTiffFd(inputFd, outputFd, options) { processedPixels, totalPixels ->
progress(processedPixels.toDouble() / totalPixels)
}
println("\n\t-----> Saved: $saved")
}
}
}
I've tried all combinations of r,rw,rwt
modes on both files as well as .jpg, .jpeg, .tif, and .tiff
extensions with no luck. Any help would be greatly appreciated.
Do you have read storage permission?
Also any logs with error?
Yes I have <uses-permission android::name="android.permission.READ_EXTERNAL_STORAGE" />
in the AndroidManifest file. I added it right above the application information.
I've also tried <uses-permission android::name="android.permission.MANAGE_EXTERNAL_STORAGE" />
and <uses-permission android::name="android.permission.WRITE_EXTERNAL_STORAGE" />
but none of those helped either.
Here are the error logs I'm getting.
FATAL EXCEPTION: main
Process: com.example.pngtotiffconverter, PID: 24601
java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=1606289597, result=-1, data=Intent { dat=content://media/... flg=0x41 clip={image/* video/* {U(content)}} }} to activity {com.example.pngtotiffconverter/com.example.pngtotiffconverter.MainActivity}: org.beyka.tiffbitmapfactory.exceptions.CantOpenFileException: Can't open file with file descriptor 103
at android.app.ActivityThread.deliverResults(ActivityThread.java:5526)
at android.app.ActivityThread.handleSendResult(ActivityThread.java:5565)
at android.app.servertransaction.ActivityResultItem.execute(ActivityResultItem.java:67)
at android.app.servertransaction.ActivityTransactionItem.execute(ActivityTransactionItem.java:45)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:139)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:96)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2443)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:205)
at android.os.Looper.loop(Looper.java:294)
at android.app.ActivityThread.main(ActivityThread.java:8176)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:971)
Caused by: org.beyka.tiffbitmapfactory.exceptions.CantOpenFileException: Can't open file with file descriptor 103
at org.beyka.tiffbitmapfactory.TiffConverter.convertJpgTiffFd(Native Method)
at com.example.pngtotiffconverter.TiffConverterObject.convertToTiffAndSaveInCache(TiffConverter.kt:37)
at com.example.pngtotiffconverter.MainActivityKt$Greeting$galleryLauncher$1.invoke(MainActivity.kt:84)
at com.example.pngtotiffconverter.MainActivityKt$Greeting$galleryLauncher$1.invoke(MainActivity.kt:70)
at androidx.activity.compose.ActivityResultRegistryKt$rememberLauncherForActivityResult$1.invoke$lambda-0(ActivityResultRegistry.kt:106)
at androidx.activity.compose.ActivityResultRegistryKt$rememberLauncherForActivityResult$1.$r8$lambda$dCDOHypJPJiF_gC4kcw2C1QNWFU(Unknown Source:0)
at androidx.activity.compose.ActivityResultRegistryKt$rememberLauncherForActivityResult$1$$ExternalSyntheticLambda0.onActivityResult(Unknown Source:2)
at androidx.activity.result.ActivityResultRegistry.doDispatch(ActivityResultRegistry.java:418)
at androidx.activity.result.ActivityResultRegistry.dispatchResult(ActivityResultRegistry.java:375)
at androidx.activity.ComponentActivity.onActivityResult(ComponentActivity.java:777)
at android.app.Activity.dispatchActivityResult(Activity.java:8943)
at android.app.ActivityThread.deliverResults(ActivityThread.java:5519)
at android.app.ActivityThread.handleSendResult(ActivityThread.java:5565)
at android.app.servertransaction.ActivityResultItem.execute(ActivityResultItem.java:67)
at android.app.servertransaction.ActivityTransactionItem.execute(ActivityTransactionItem.java:45)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:139)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:96)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2443)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:205)
at android.os.Looper.loop(Looper.java:294)
at android.app.ActivityThread.main(ActivityThread.java:8176)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:971)
These are the crash logs I get when I remove the close()
functions for the inputFD and outputFD respectively. I think these are more accurate than the ones I've posted above. Both methods crash within TiffBitmapFactory.
fdsan: failed to exchange ownership of file descriptor: fd 101 is owned by ParcelFileDescriptor 0xce5ad5, was expected to be unowned
Fatal signal 6 (SIGABRT), code -1 (SI_QUEUE) in tid 29897 (totiffconverter), pid 29897 (totiffconverter)
Cmdline: com.example.pngtotiffconverter
pid: 29897, tid: 29897, name: totiffconverter >>> com.example.pngtotiffconverter <<<
#02 pc 00000000000200b4 /data/app/~~MVx-QyN8xxbS8x42DsS4LQ==/com.example.pngtotiffconverter-DsfEtC9kGM2_p0PKw01eSQ==/base.apk!libtiffconverter.so (offset 0xd5000) (JpgToTiffConverter::convert()+232) (BuildId: df448798ff7fc0449147e9e7908310cbf5fdb9fe)
#03 pc 000000000001bd64 /data/app/~~MVx-QyN8xxbS8x42DsS4LQ==/com.example.pngtotiffconverter-DsfEtC9kGM2_p0PKw01eSQ==/base.apk!libtiffconverter.so (offset 0xd5000) (Java_org_beyka_tiffbitmapfactory_TiffConverter_convertJpgTiffFd+100) (BuildId: df448798ff7fc0449147e9e7908310cbf5fdb9fe)
#09 pc 0000000000005784 /data/data/com.example.pngtotiffconverter/code_cache/.overlay/base.apk/classes3.dex (com.example.pngtotiffconverter.TiffConverterObject.convertToTiffAndSaveInCache+0)
#14 pc 00000000000048f8 /data/data/com.example.pngtotiffconverter/code_cache/.overlay/base.apk/classes3.dex (com.example.pngtotiffconverter.MainActivityKt$Greeting$galleryLauncher$1.invoke+0)
#19 pc 00000000000048b0 /data/data/com.example.pngtotiffconverter/code_cache/.overlay/base.apk/classes3.dex (com.example.pngtotiffconverter.MainActivityKt$Greeting$galleryLauncher$1.invoke+0)
#24 pc 000000000017e3dc /data/data/com.example.pngtotiffconverter/code_cache/.overlay/base.apk/classes.dex (androidx.activity.compose.ActivityResultRegistryKt$rememberLauncherForActivityResult$1.invoke$lambda-0+0)
#29 pc 000000000017e394 /data/data/com.example.pngtotiffconverter/code_cache/.overlay/base.apk/classes.dex (androidx.activity.compose.ActivityResultRegistryKt$rememberLauncherForActivityResult$1.$r8$lambda$dCDOHypJPJiF_gC4kcw2C1QNWFU+0)
#34 pc 000000000017e2bc /data/data/com.example.pngtotiffconverter/code_cache/.overlay/base.apk/classes.dex (androidx.activity.compose.ActivityResultRegistryKt$rememberLauncherForActivityResult$1$$ExternalSyntheticLambda0.onActivityResult+0)
#39 pc 00000000001801b4 /data/data/com.example.pngtotiffconverter/code_cache/.overlay/base.apk/classes.dex (androidx.activity.result.ActivityResultRegistry.doDispatch+0)
#44 pc 000000000017ffd0 /data/data/com.example.pngtotiffconverter/code_cache/.overlay/base.apk/classes.dex (androidx.activity.result.ActivityResultRegistry.dispatchResult+0)
#49 pc 000000000017ccd0 /data/data/com.example.pngtotiffconverter/code_cache/.overlay/base.apk/classes.dex (androidx.activity.ComponentActivity.onActivityResult+0)
if you get want to use File descriptor from ParcelFileDescriptor, you first need to detach the file descriptor:
int fd = parcheFileDescriptor.detachFd(); // return the fileDescriptor int that can be used in native code like saveBitmap()
// use *fd* to call saveBitmap()