0

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
Xvolks
  • 2,065
  • 1
  • 21
  • 32

2 Answers2

1

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

Artyom Degtyarev
  • 2,704
  • 8
  • 23
  • Could you explain the point of the `memScoped` block? It seems to be used to free a memory block allocated by a call to `alloc`. There is no such explicit call here. – Xvolks Jul 22 '19 at 09:35
  • Hmm, according to [this](https://github.com/JetBrains/kotlin-native/blob/master/INTEROP.md#passing-pointers-to-bindings) paragraph, there is no need to use `memScoped`. Let's assume that it can be removed, I'll edit the answer. – Artyom Degtyarev Jul 22 '19 at 12:23
0

Edit: see @ArtyomDegtyarev's answer.

Answering my own question if anybody bumps into the same issue.

The trick is to create an array of pointers of the required size (here 1) and to assign the returned pointer to the right element of the array (here 0).

Then call toCValues() on the array to get the &pointer C equivalent.

fun usePix(filename: String) {
    memScoped {
        val ppix = arrayOfNulls<CPointer<PIX>?>(1)
        val pix = pixRead(filename) ?: throw NullPointerException("Pix is null")
        ppix[0] = pix
        doSomethingWithPix(pix.pointed)
        pixDestroy(ppix.toCValues())
    }
}

I'm not sure that the memScoped block is useful here it was written in the source I've found.

EDIT: I expect that there is more natural way to do this. Should Kotlin-Native have a way to get the address of any variable? I think so. Is it a limitation of the language, a work in progress? If anybody has the answer, I would be glad to hear it.

Xvolks
  • 2,065
  • 1
  • 21
  • 32