Beyka / Android-TiffBitmapFactory

MIT License
133 stars 46 forks source link

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

Open pasllani opened 1 year ago

pasllani commented 1 year ago

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.

Beyka commented 1 year ago

Do you have read storage permission?

Beyka commented 1 year ago

Also any logs with error?

pasllani commented 1 year ago

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 commented 1 year ago

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)
sonique6784 commented 7 months ago

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