Android-TiffBitmapFactory icon indicating copy to clipboard operation
Android-TiffBitmapFactory copied to clipboard

TiffConverter.convertJpgTiffFd fails to convert image with a CantOpenFileException from cache.

Open pasllani opened this issue 1 year ago • 5 comments

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: cache

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.

pasllani avatar Aug 22 '23 17:08 pasllani

Do you have read storage permission?

Beyka avatar Aug 23 '23 06:08 Beyka

Also any logs with error?

Beyka avatar Aug 23 '23 06:08 Beyka

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) 

pasllani avatar Aug 23 '23 08:08 pasllani

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)

pasllani avatar Aug 23 '23 19:08 pasllani

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

sonique6784 avatar Apr 27 '24 11:04 sonique6784