Search code examples
kotlinleptonicakotlin-native

Kotlin-native C-interop with leptonica issue


I'm trying to use leptonica library from Kotlin-native. I've managed to create the klib and basic code is working.

My problem is :

  • I load an image with pixRead() --> OK,
  • Use the image --> OK,
  • I'm unable to call pixDestroy() on that image --> FAIL.

Without the pixDestroy() call the program works as expected, except that it leaks memory.

Basically I want to get the address of the pointer like this (source):

    pixt1 = pixRead("/tmp/lept/dewmod/0020.png");
    pixWrite("/tmp/lept/dewtest/006.png", pixt1, IFF_PNG);
    pixDestroy(&pixt1);

My code looks like :

import leptonica.* 
import kotlinx.cinterop.* 


fun doSomethingWithPix(pix: PIX) {
    // bla
    println(pix.xres) 
}

fun usePix(filename: String) {
    val pix = pixRead(filename) ?: throw NullPointerException("Pix is null")
    doSomethingWithPix(pix.pointed)
    pixDestroy(pix ???) // Expect a CValuesRef<CPointerVar<PIX>> how to create/cast that ? 
}

fun main() {
    usePix("test.png") }
}

For the record here is my leptonica.def file.

headers = leptonica/allheaders.h
headerFilter = leptonica/allheaders.h
package = leptonica

compilerOpts.osx = -I/usr/local/opt/include
linkerOpts.osx = -L/usr/local/opt/lib -llept

The build.gradle

plugins {
    id 'org.jetbrains.kotlin.multiplatform' version '1.3.41'
}
repositories {
    mavenCentral()
}
kotlin {
    // For ARM, should be changed to iosArm32 or iosArm64
    // For Linux, should be changed to e.g. linuxX64
    // For MacOS, should be changed to e.g. macosX64
    // For Windows, should be changed to e.g. mingwX64
    macosX64("macos") {
        compilations.main.cinterops {
            png
            tesseract
            leptonica
        }

        binaries {
            executable {
                // Change to specify fully qualified name of your application's entry point:
               entryPoint = 'sample.main'
                // Specify command-line arguments, if necessary:
                runTask?.args('')
            }
        }
    }
//    iosArm64("ios64") {
//        compilations.main.cinterops {
//            png
//        }
//
//        binaries {
//            executable {
//                // Change to specify fully qualified name of your application's entry point:
//               entryPoint = 'sample.main'
//                // Specify command-line arguments, if necessary:
//                runTask?.args('')
//            }
//        }
//    }
    sourceSets {
        // Note: To enable common source sets please comment out 'kotlin.import.noCommonSourceSets' property
        // in gradle.properties file and re-import your project in IDE.
        macosMain {
        }
        macosTest {
        }
    }

}

// Use the following Gradle tasks to run your application:
// :runReleaseExecutableMacos - without debug symbols
// :runDebugExecutableMacos - with debug symbols

Solution

  • You can convert your variable like this:

    fun usePix(filename: String) {
        val pix = pixRead(filename) ?: throw NullPointerException("Pix is null")
        doSomethingWithPix(pix.pointed)
        pixDestroy(cValuesOf(pix))
    }
    

    I found this solution in the documentation, it can be found here